背景
靜態(tài)網(wǎng)站托管(s3website)相比普通s3請(qǐng)求,多了幾個(gè)核心功能:
- 默認(rèn)請(qǐng)求頁面。當(dāng)用戶url以"/"結(jié)尾時(shí)返回的頁面。
- 錯(cuò)誤頁面。當(dāng)用戶請(qǐng)求的url發(fā)生錯(cuò)誤時(shí)返回的頁面。
- 轉(zhuǎn)發(fā)設(shè)置。針對(duì)一個(gè)bucket或者bucket下某個(gè)對(duì)象設(shè)置請(qǐng)求跳轉(zhuǎn)。
除此之外,s3website模式和s3模式相差不大。
設(shè)置方法
- 使用s3cmd設(shè)置bucket為靜態(tài)網(wǎng)站托管模式方法如下:
s3cmd ws-create --ws-index=<default.html> --ws-error=<error.html> s3://<bucket-name>
達(dá)到的效果就是當(dāng)用戶訪問http://<endpoint>/<bucket-name>/
時(shí),會(huì)返回s3://<bucket-name>/<default.html>
對(duì)象,更一般的情況是用戶訪問http://<endpoint>/<bucket-name>/<path>/
時(shí),實(shí)際返回s3://<bucket-name>/<path>/<default.html>
對(duì)象;如果用戶訪問的url沒有對(duì)應(yīng)的s3 object,則返回s3://<bucket-name>/<error.html>
對(duì)象。
- 使用REST API設(shè)置bucket為靜態(tài)網(wǎng)站托管的方法如下:
#!/bin/bash
host=<ip>:<normal-rgw-port>
resource="/<bucket_name>"
indexPage="<index.html>"
errorPage="<error.html>"
contentType="XXYYZZ"
dateValue=`date -R -u`
stringToSign="PUT
${contentType}
${dateValue}
${resource}"
s3Key=<key>
s3Secret=<secret>
#計(jì)算簽名
signature=`/bin/echo -n "$stringToSign" | openssl sha1 -hmac ${s3Secret} -binary | base64`
curl -X PUT \
-H "Date: ${dateValue}" \
-H "Content-Type: ${contentType}" \
-H "Authorization: AWS ${s3Key}:${signature}" \
-d "<WebsiteConfiguration><IndexDocument><Suffix>${indexPage}</Suffix></IndexDocument><ErrorDocument><Key>${errorPage}</Key></ErrorDocument><RoutingRules><RoutingRule><Condition><KeyPrefixEquals>docs/</KeyPrefixEquals></Condition><Redirect><ReplaceKeyPrefixWith>documents/</ReplaceKeyPrefixWith></Redirect></RoutingRule></RoutingRules></WebsiteConfiguration>" \
"http://${host}${resource}?website"
注意:相應(yīng)的
GET
,DEL
HTTP方法可用于獲取和清空bucket的靜態(tài)網(wǎng)站托管配置。
接收請(qǐng)求的RGW需要手動(dòng)配置的配置項(xiàng)有:
rgw_enable_apis = "s3, s3website, swift, swift_auth, admin"
rgw_enable_static_website = true
使用靜態(tài)網(wǎng)站托管bucket時(shí)需要向特定的RGW發(fā)送請(qǐng)求,這些RGW的配置和常規(guī)用于s3訪問的RGW不同:
rgw_enable_apis="s3website, s3, swift, swift_auth, admin"
代碼實(shí)現(xiàn)
設(shè)置bucket
將bucket設(shè)置成靜態(tài)網(wǎng)站托管模式時(shí),使用的rgw_api仍然是s3
,和常規(guī)對(duì)象存儲(chǔ)相同。
url中的website
請(qǐng)求參數(shù)會(huì)標(biāo)識(shí)本次請(qǐng)求是一次“設(shè)置bucket為website模式”的請(qǐng)求:
RGWOp *RGWHandler_REST_Bucket_S3::op_put()
{
...
if (s->info.args.sub_resource_exists("website")) {
if (!s->cct->_conf->rgw_enable_static_website) {
return NULL;
}
return new RGWSetBucketWebsite_ObjStore_S3;
}
...
使用bucket
為了以website模式對(duì)一個(gè)bucket進(jìn)行訪問,需要將接收請(qǐng)求的RGW的rgw_api設(shè)置成s3website
優(yōu)先。當(dāng)RGW接收到請(qǐng)求后,會(huì)將請(qǐng)求標(biāo)志成s3website模式
int RGWREST::preprocess(struct req_state *s, RGWClientIO* cio)
{
...
if (s3website_enabled && api_priority_s3website > api_priority_s3) {
in_hosted_domain_s3website = 1;
}
if (in_hosted_domain_s3website) {
s->prot_flags |= RGW_REST_WEBSITE;
}
...
}
在隨后的RGWHandler選擇中會(huì)據(jù)此選用相應(yīng)的Handler
RGWHandler_REST* RGWRESTMgr_S3::get_handler(struct req_state *s)
{
bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
int ret = RGWHandler_REST_S3::init_from_header(s,
is_s3website ? RGW_FORMAT_HTML :
RGW_FORMAT_XML, true);
if (ret < 0)
return NULL;
RGWHandler_REST* handler;
// TODO: Make this more readable
if (is_s3website) {
if (s->init_state.url_bucket.empty()) {
handler = new RGWHandler_REST_Service_S3Website;
} else if (s->object.empty()) {
handler = new RGWHandler_REST_Bucket_S3Website;
} else {
handler = new RGWHandler_REST_Obj_S3Website;
}
...
RGWHandler_REST_Service_S3Website
, RGWHandler_REST_Bucket_S3Website
和RGWHandler_REST_Object_S3Website
均繼承自RGWHandler_REST_S3Website
, 三者在get_obj失敗時(shí)便會(huì)調(diào)用RGWHandler_REST_S3Website::error_handler()
,從而實(shí)現(xiàn)錯(cuò)誤界面的返回。
默認(rèn)頁面的返回是由RGWHandler_REST_S3Website::retarget()
實(shí)現(xiàn),此函數(shù)在rgw_process.cc中被調(diào)用:
int process_request(RGWRados* store, RGWREST* rest, RGWRequest* req,
RGWStreamIO* client_io, OpsLogSocket* olog)
{
...
/**
* Only some accesses support website mode, and website mode does NOT apply
* if you are using the REST endpoint either (ergo, no authenticated access)
*/
req->log(s, "recalculating target");
ret = handler->retarget(op, &op);
...
因此retarget動(dòng)作永遠(yuǎn)會(huì)執(zhí)行,不論是s3還是s3website。
總結(jié)
在RGW中使用靜態(tài)網(wǎng)站托管功能需要配置兩套R(shí)GW示例,分別用于配置bucket和訪問bucket。且默認(rèn)頁面需要在沒個(gè)“目錄”下放置(這一點(diǎn)可能和其他廠商的對(duì)象存儲(chǔ)服務(wù)有區(qū)別),否則會(huì)導(dǎo)致404,進(jìn)而返回錯(cuò)誤頁面。