私は素敵な 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>
問題
- 元のソースが表示されます (おそらく、私の php は錆びています...)リクエストを Twitter に転送するときに名前が付けられたヘッダー
Expect
が含まれており、私が知る限り、値は空です。このヘッダーを含めると、次の応答が返されます:417 Expectation Failed
. - 代わりに
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);
FALSE
cURL によるピアの証明書の検証を停止します。オプションを使用して検証する代替証明書を指定する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はそれを行います)が、ドキュメントは意味がありません。
したがって、これはカールの設定を問題の可能性から除外しているようです。