5

私は素敵な Twitter API ラッパーを使用しています: codebird-js ; また、Twitter の API では CORS が許可されていないため、CORS AJAX リクエストを作成する必要がある場合に備えて、プロキシが含まれています (私はそうしています)。

余談ですが、Twitter はこのプロキシで問題ありませんか? Codebird は推奨ライブラリにリストされているため、そう推測する必要があります。

プロキシは開発者のサーバーの 1 つを通過しますが、これで十分ですが、時には数時間、時には丸一日ダウンすることもあります。アプリが本番環境に移行すると、これは受け入れられなくなるため、プロキシを自己ホストして制御を強化する必要があります。

幸いなことに、それらはプロキシのソースも提供します。残念ながら、PHP はオプションではありません。だから私はそれをCFMLに移植しようとしています.CFMLは私の現在の選択肢の中で最高です(Node.jsとRubyも検討できますが、どちらにもあまり慣れていないため、今のところCFMLを選択しました)

要するに、このスクリプトを CFMLに移植しようとしているということです。以下は私がこれまでに持っているものですが、コードの下に説明する問題があります。

<cfscript>
    try{
        header(name="Access-Control-Allow-Origin", value="*");
        header(name="Access-Control-Allow-Headers", value="Origin, X-Authorization");
        header(name="Access-Control-Allow-Methods", value="POST, GET, OPTIONS");

        method = cgi.request_method;
        if (method == 'OPTIONS'){
            abort;
        }

        path = 'https://api.twitter.com' & cgi.path_info;
        headers = [{name="Expect", value=""}];
        req_headers = getHTTPRequestData().headers;
        req_body = getHTTPRequestData().content;

        if (isBinary(req_body)){
            req_body = charsetEncode(req_body, "UTF-8");
        }

        if (structKeyExists(req_headers, 'X-Authorization')){
            arrayAppend(headers, { name='Authorization', value=req_headers['X-Authorization'] });
        }

        response = http_wrapper(method, path, headers, req_body);
        code = val( response.statusCode );
        msg = trim( replace(response.statusCode, code, '') );

        twitter_headers = listToArray(structKeyList( response.responseHeader ));
        for (i = 1; i <= arrayLen(twitter_headers); i++){
            if (twitter_headers[i] == 'set-cookie'){ continue; }
            header(name=twitter_headers[i], value=response.responseHeader[twitter_headers[i]]);
        }
        header(statusCode=code, statusText=msg);
        respond(response.filecontent);
    }catch(any e){
        application.bugService.notifyService(
            message = "Error in Twitter Proxy"
            ,severityCode = "ERROR"
            ,exception = e
        );
        header(statusCode=500,statusText="Proxy Error");
        writeDump(var={error=e,twitter_response=response}, format='text');
    }
</cfscript>

<cffunction name="http_wrapper">
    <cfargument name="method" />
    <cfargument name="path" />
    <cfargument name="headers" />
    <cfargument name="body" />

    <cfset var local = {} />

    <cfhttp method="#arguments.method#" url="#arguments.path#" result="local.result">
        <cfloop from="1" to="#arrayLen(arguments.headers)#" index="local.i">
            <cfhttpparam type="header" name="#arguments.headers[i].name#" value="#arguments.headers[i].value#" />
        </cfloop>
        <cfhttpparam type="body" value="#arguments.body#" />
    </cfhttp>

    <cfreturn local.result />
</cffunction>

<cffunction name="header">
    <cfheader attributeCollection="#arguments#" />
</cffunction>

<cffunction name="respond">
    <cfargument name="body" />
    <cfcontent reset="true" /><cfoutput>#body#</cfoutput><cfabort/>
</cffunction>

問題

  1. 元のソースが表示されます (おそらく、私の php は錆びています...)リクエストを Twitter に転送するときに名前が付けられたヘッダーExpectが含まれており、私が知る限り、値は空です。このヘッダーを含めると、次の応答が返されます: 417 Expectation Failed.
  2. 代わりにExpectヘッダーを省略した場合、プロキシされたリクエストに対して 401 応答が返されます。

提供された PHP プロキシに対してテストされ、正常に動作するクライアント コードを使用していることに注意することが重要です。独自のプロキシを使用するために、Codebird インスタンスを次のようにセットアップしました。

var cb = new Codebird;
cb.setProxy('https://mydomain.com/api/v1/proxy/twitter.cfm/');
cb.setConsumerKey(key, secret);

ネットワーク リクエストは、Chrome デバッグ ツールでは次のようになります。

リクエスト URL: https://mydomain.com/api/v1/proxy/twitter.cfm/oauth/request_token
リクエスト方法: POST
ステータス コード: 401 Unauthorized
リクエスト ヘッダー
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept -言語: en-US、en;q=0.8
接続:キープアライブ
Content-Length: 61
Content-Type: application/x-www-form-urlencoded
Host: mydomain.com
Origin: null
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (Gecko のような KHTML) Chrome/28.0.1500.95 Safari/537.36
X-Authorization:OAuth oauth_consumer_key="..........", oauth_nonce="PqK4KPCc", oauth_signature="B%2BQ..............b08%3D", oauth_signature_method=" HMAC-SHA1", oauth_timestamp="1376507615", oauth_version="1.0"
フォームデータ:
oauth_callback: http://mydomain.com/twitter-login

X-Authorization ヘッダーがリクエストに含まれており、私の知る限り、完全で正しいことがわかります。最初のコード サンプルでは、​​このリクエスト ヘッダーが Authorization ヘッダーとして Twitter に転送されていることがわかります。 .

401 応答が返される理由がわかりません。ドキュメントは、Expect ヘッダーが必要であることを示していないようです。そのため、無視できると想定しています。(確かに、PHP に関する私の知識の限りでは、実際には PHP バージョンでも送信されていません...)

しかし、その場合、なぜ 401 が返されるのでしょうか? 他のすべては私には正しいようです...

更新: CURL オプション

Adam のコメントによると、設定されている CURL オプションを故意に無視したことに注意するのを忘れていました (CFHTTP がすべてを処理してくれると思いました)。ここで、使用されているそれぞれを調べて文書化し、ベースが CFHTTP でカバーされていることを確認します。


curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

TRUE転送を直接出力するのではなく、curl_exec() の戻り値の文字列として返します。

これはよく書かれていないドキュメントです (CF も時々罪を犯します) が、このオプションを設定すると、結果が応答バッファーに追加される代わりに返されることを示しているようです。小切手; CFHTTP はデフォルトでこれを行います。


curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);

TRUEサーバーが HTTP ヘッダーの一部として送信する "Location: " ヘッダーに従います (これは再帰的であることに注意してください。CURLOPT_MAXREDIRS が設定されていない限り、PHP は送信された "Location: " ヘッダーと同じ数に従います)。

同等の CFHTTP 設定はredirect="true"(デフォルトで true) です。PHP バージョンはリダイレクトに従わないと言っているため、これは不一致です。これを心に留めておきますが、これが私の 401 の原因であるとは思えません。


curl_setopt($ch, CURLOPT_HEADER, 1);

TRUE出力にヘッダーを含めます。

CFHTTP の応答にヘッダーが含まれていることを確認してください。


curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);

FALSEcURL によるピアの証明書の検証を停止します。オプションを使用して検証する代替証明書を指定するCURLOPT_CAINFOか、オプションを使用して証明書ディレクトリを指定できCURLOPT_CAPATHます。

CFHTTP は SSL 証明書を検証します。コメントで述べたように、必要な証明書は既にインポートされているようです。


curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

1 は、SSL ピア証明書内の共通名の存在を確認します。2 共通名の存在を確認し、それが提供されたホスト名と一致することも確認します。本番環境では、このオプションの値は 2 (デフォルト値) のままにしておく必要があります。

これが何をチェックしているのかはっきりとはわかりませんが、CFHTTP に関連する設定があるとは思えないので、今のところ、このチェックが行われている (または問題の要因ではない) と仮定します。


curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');

ピアを検証するための 1 つ以上の証明書を保持するファイルの名前。これは、 と組み合わせて使用​​する場合にのみ意味がありCURLOPT_SSL_VERIFYPEERます。

前にコメントで述べたように、プロキシ ソースで提供された PEM ファイルは既に構成に含まれており、検証中です。


curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

設定する HTTP ヘッダー フィールドの配列。array('Content-type: text/plain', 'Content-length: 100')

チェック: ヘッダーを Twitter に渡しています。


curl_setopt($ch, CURLINFO_HEADER_OUT, 1);

TRUEハンドルのリクエスト文字列を追跡します。

何?設定名に基づいて、これは出力にヘッダーを含めることを意味すると思います(チェック、CFHTTPはそれを行います)が、ドキュメントは意味がありません。


したがって、これはカールの設定を問題の可能性から除外しているようです。

4

2 に答える 2

1

質問はコードの移植に関するものだったので、これを答えとして受け入れるつもりはありません。これは、変換されたコードで発生した問題を確実に解決するものではありません。しかし、同じ苦境にある他の誰かに役立つと思ったので、私がしたこととその理由をここに記録します.


今日、Heroku が PHP をサポートしていることを思い出したので、そこで元の PHP プロジェクトのインスタンスをセットアップしたところ、完全に機能しています。CFML ポートが機能しないことにまだ戸惑っていますが、少なくとも、Heroku でホストされているプロキシを使用できることと、そのアップタイムがランダムな Web サイトよりもはるかに安全であることを知っているので、プロジェクトを続行できます。

結局のところ、ホストが自分の Twitter 資格情報をログに記録したり悪用したりしないように (完全ではありませんが) ある程度の信頼を置いていますが、それらを途中で使用すると、少し不安になりました。github レポジトリに投稿されたソース コードは良さそうですが、それが機能する限り、私は PHP に反対するものは何もありません! 私の問題の根底にあるのは、99% が制御不能な稼働時間で、1% がセキュリティでした。

単一の無料の Heroku dyno で無期限に実行できることも害にはなりません。<3 ヘロク

誰かがこれに出くわして同じことをしたい場合に備えて、私が従った手順は次のとおりです(コマンドライン)。最初にHeroku ツールベルトをインストールする必要があることに注意してください。

$ git clone https://github.com/mynetx/codebird-cors-proxy.git twitter-cors-proxy-php
Cloning into 'twitter-cors-proxy-php'...
remote: Counting objects: 52, done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 52 (delta 23), reused 47 (delta 18)
Unpacking objects: 100% (52/52), done.

$ cd twitter-cors-proxy-php
$ rm -rf .git
$ ls -al
total 88
drwxr-xr-x   7 adam  staff    238 Aug 15 11:14 .
drwxr-xr-x  57 adam  staff   1938 Aug 15 11:11 ..
-rw-r--r--   1 adam  staff    250 Aug 15 11:11 CHANGELOG
-rw-r--r--   1 adam  staff  35147 Aug 15 11:11 LICENSE
-rw-r--r--   1 adam  staff   1531 Aug 15 11:11 README.md
drwxr-xr-x   5 adam  staff    170 Aug 15 11:11 src

$ rm -f CHANGELOG LICENSE README.md
$ mv src/* ./
$ rm -rf src
$ mv codebird-cors-proxy.php index.php
$ git init
Initialized empty Git repository in /Users/adam/DEV/twitter-cors-proxy-php/.git/

$ git add .
$ git st
## Initial commit on master
A  cacert.pem
A  index.php

$ git commit -am"initial import from codebird"
[master (root-commit) 4196e98] initial import from codebird
 2 files changed, 4115 insertions(+)
 create mode 100644 cacert.pem
 create mode 100644 index.php

$ heroku apps:create twitter-cors-proxy
Creating twitter-cors-proxy... done, stack is cedar
http://twitter-cors-proxy.herokuapp.com/ | git@heroku.com:twitter-cors-proxy.git
Git remote heroku added

$ git push heroku master
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 134.24 KiB, done.
Total 4 (delta 0), reused 0 (delta 0)

-----> PHP app detected
-----> Bundling mcrypt version 2.5.8
-----> Bundling Apache version 2.2.25
-----> Bundling PHP version 5.3.27
-----> Discovering process types
       Procfile declares types -> (none)
       Default types for PHP   -> web

-----> Compiled slug size: 22.3MB
-----> Launching... done, v3
       http://twitter-cors-proxy.herokuapp.com deployed to Heroku

To git@heroku.com:twitter-cors-proxy.git
 * [new branch]      master -> master

次に、新しいプロキシを次のように Codebird に設定します。

var cb = new Codebird;
cb.setProxy('https://twitter-cors-proxy.herokuapp.com/index.php');

...そして、すべてが世界で正しかった!

于 2013-08-15T15:52:20.880 に答える
0

Twitter API をどれだけ調査したかわかりませんが、パブリック リクエストのサポートは終了しており、登録済みのアプリを使用してすべてを承認する必要があります。ここで登録できますdev.Twitter

私はあなたがここで何を達成しようとしているのかを見ることができます。Twitter API への動作中の http 呼び出しをデモし、キーとシークレットを追加する基本的な CFC があり、twitterEndpoint 変数をさまざまなオプションに変更して、さまざまな応答を確認できます。- お役に立てれば。

基本的な Twitter CFC - これは OpenBD CFML エンジンでのみテストされていることに注意してください

于 2013-08-15T08:15:22.363 に答える