8
* 0.0.0.2 鯖上以外でインポートした時のバグ修正
12
* License: Public Domain
14
import nazo.string, std.stdio, nazo.zlib, std.conv, std.regexp, nazo.savelist, nazo.stdio, nazo.env, nazo.log, std.uri, std.cstream, std.thread, std.conv;
18
* Bugs: multipart/form-dataはテストしてない。
20
* port80以外やhttps使用時は未テスト(問題が起こる可能性が高い)
34
private static string scriptname;
35
private static string bin;
36
static string getScriptName(){
39
private static bool isnph;
43
static methods method;
45
static void securityError(){
46
HttpResponse.write("Security Error!",403);
48
///そのAcceptには対応してませんというエラーを吐く
49
static void acceptError(){
50
HttpResponse.write("そのAcceptには対応してませんよん",406);
52
///リクエストがGETとPOST以外ならfalseを返す。(通常HEAD以外はありえないと思われ(PUTやDELETE、TARCEはApacheが防いでくれるとおも)。HEADは攻撃に役立つので防いどく)
53
static bool isAllowMethod(){
54
return cast(bool)(method==methods.POST||method==methods.GET);
57
static bool isGetMethod(){
58
return cast(bool)(method==methods.GET);
61
static bool isPostMethod(){
62
return cast(bool)(method==methods.POST);
64
///POST向けのこれ自身から呼び出されたかどうかをチェック
65
static bool isValidReferer(){
66
string referer=Env.TGet!("HTTP_REFERER")();
71
string schema=referer.getBefore(':');
72
if(schema!="http"&&schema!="https")return false;
73
referer=referer[schema.length+3..$];
74
string refhost=getBefore(referer,'/');
75
string tmp=getBefore(referer[refhost.length..$],'?');
76
refhost=getBefore(refhost,':');//XXX 未テスト
77
if(refhost==Env.TGet!("SERVER_NAME")())
78
if(startWith(tmp,getScriptName))
82
///Content-Typeのリストからどれを使うかを選ぶ
83
///Reterns: 何返していいか分からなかったら空文字列を返します。
84
static string allowAccept(string[] lst){
85
if(accept=="")return "";
87
getBefores(accept,',',delegate void(string i){acceptArray~=strip(getBefore(i,';'));});
88
foreach(string type;lst){
89
foreach(string i;acceptArray){
98
///Bugs: リストからどれを選ぶか方式にした方がいいが面倒臭い。
99
static string acceptLanguage(){
100
return acceptlanguage;
102
/+ ///キャラクタリストからどれを使うかを選ぶ
104
string acceptCharset(string[] lst){
105
return this.Env.TGet!("HTTP_ACCEPT_CHARSET")();
107
///エンコードのリストからどれを使うかを選ぶ
108
///Returns: lstの中に含まれていた場合はそれを、そうでない場合は""を返す。
110
string acceptEncoding(string[] lst){
111
return this.Env.TGet!("HTTP_ACCEPT_CHARSET")();
114
static string reqURI(){
115
return Env.TGet!("REQUEST_URI")();
117
private static string pathinfo;
119
///XXX: パスの正規化が必要かもしれない。
120
static string pathInfo(){
122
pathinfo=Env.TGet!("PATH_INFO")();
123
// RegExp re=new RegExp("(//|/./|/[^/]*/../)","g");
124
// info=re.replace(info,"/");
129
///Bugs: ポートやhttps等に未対応
130
static string path(){
131
return "http://"~Env.TGet!("SERVER_NAME")()~getScriptName;
133
private static string relpath;
135
static string relPath(){
138
string path=pathInfo();
141
getBeforesWithoutLast(path,'/',delegate void(string t){len+=3;});
143
for(int i;i<dir.length;i+=3){
147
if(getScriptName=="")throw new Exception("環境変数 SCRIPT_NAME が空です。");*/
148
relpath=basePath()~bin;
152
private static string basepath;
154
static string basePath(){
157
string path=pathInfo();
160
getBeforesWithoutLast(path,'/',delegate void(string t){len+=3;});
162
for(int i;i<dir.length;i+=3){
166
basepath=to!(string)(dir);
171
static string cookie(){
172
return Env.TGet!("HTTP_COOKIE")();
176
return Env.TGet!("REMOTE_ADDR")();
179
///Safariが引っかかってるけど気にしない。
181
string ua=Env.TGet!("HTTP_USER_AGENT")().tolower();
182
if(ua=="")return true;
184
for(i=ua.length-3;i > ua.length-5;i--){
185
if(ua[i]=='b'&&ua[i..i+3]=="bot")
191
if(ua[i..i+3]=="bot")
195
if(ua[i..i+5]=="slurp")
201
// if(ua==""||std.string.find(ua,"bot")!=-1||std.string.find(ua,"slurp")!=-1)return true;
202
if(accept==""||accept=="*/*")return true;
205
private static string accept;
206
private static string acceptlanguage;
208
debug(HTTPLOG) static void writeLog(Log log,string flag){
209
log.write(flag~":"~Env.get("REQUEST_METHOD")~"\t"~Env.get("SERVER_NAME")~getScriptName~Env.get("PATH_INFO")~"?"~Env.get("QUERY_STRING")~"\t"~Env.get("HTTP_USER_AGENT")~"\t"~Env.get("HTTP_ACCEPT")~"\t"~Env.get("HTTP_ACCEPT_LANGUAGE"));
212
static string[string] query;
214
static File*[string] files;
216
private static Http m;
217
///インスタンスの取得(Singleton)
224
//HTTPを扱うのを作成するコンストラクタ
226
scriptname=Env.TGet!("SCRIPT_NAME")();
227
if(scriptname.length==0)return null;
228
bin=getAfter(getScriptName,'/');
229
isnph=startWith(bin,"nph-");
231
switch(Env.TGet!("REQUEST_METHOD")()){
233
querystr=Env.TGet!("QUERY_STRING")();
238
querystr=cast(string)StdIo.readStdIn(.toInt(Env.TGet!("CONTENT_LENGTH")()));
247
method=methods.DELETE;
250
method=methods.TRACE;
253
method=methods.UNKNOWN;
255
accept=Env.TGet!("HTTP_ACCEPT")();
256
acceptlanguage=Env.TGet!("HTTP_ACCEPT_LANGUAGE")();
257
if(!startWith(strip(Env.TGet!("CONTENT_TYPE")()),"multipart/form-data")){
258
getBefores(querystr,'&',delegate void(string i){
259
int s=std.string.find(i,'=');
260
if(s!=-1)Http.query[i[0..s]]=i[s+1..$];
263
//XXX 未テスト。base64には対応していない。
264
//filename='='の時問題あるかも
265
uint start=std.string.find(Env.TGet!("CONTENT_TYPE")(),"boundary=")+"boundary=".length;
266
string l="--"~Env.TGet!("CONTENT_TYPE")()[start..$];
267
querystr=querystr[l.length+"\r\n".length..$-("--".length+l.length+"--".length)-"\r\n".length];
268
string[] querylist=std.string.split(querystr,\r\n~l~\r\n);
269
foreach(string i;querylist){
270
string[] headers=std.string.split(getBefore(i,\r\n\r\n),\r\n);
272
foreach(string i2;headers){
273
switch(getBefore(i2,':')){
274
case "Content-Disposition":
275
foreach(string i3;std.string.split(i2,";")){
278
int t=std.string.find(i4,'=');
280
pref[i4[0..t]]=i4[t+2..$-1];
287
if(auto name=("filename" in pref)){
290
int s=std.string.find(i,\r\n\r\n);
291
f.data=cast(ubyte[])i[s+4..$];
292
files[pref["name"]]=f;
294
int s=std.string.find(i,\r\n\r\n);
295
Http.query[pref["name"]]=i[s+4..$];
312
static void write(string responseBody,uint status=200,string[string] headers=null){
313
write(cast(ubyte[])responseBody,status,headers);
317
static void write(ubyte[] responseBody,uint status=200,string[string] headers=null){
320
if(std.string.find(Env.TGet!("HTTP_ACCEPT_ENCODING")(),"deflate")!=-1){
322
ct=new Thread(delegate int(){responseBody=cast(ubyte[])compress(responseBody);return 0;});
325
bool isNph=Http.isNph;
327
if(isNph)header="HTTP/1.1 ";
329
if(!isNph)header="Status: ";
331
header~="304 Not Modified\r\n";
333
header~="403 Forbidden\r\n";
335
header~="404 File Not Found\r\n";
337
header~="406 Not Acceptable\r\n";
339
header~="301 Moved Permanently\r\n";
340
}else if(isNph)header~="200 OK\r\n";
341
if(isNph)header~="Connection: Keep-Alive\r\nKeep-Alive: timeout=15, max=100\r\n";
342
if(auto t="Vary" in headers){
343
*t~=",Accept-Encoding";
345
header~="Vary: Accept-Encoding\r\n";
347
foreach(string key,inout string val;headers){
348
header~=key~": "~val~"\r\n";
351
header~="Content-Encoding: deflate\r\n";
355
StdIo.writeStdOut(cast(ubyte[])header);
357
StdIo.writeStdOutWithThread(responseBody);
359
StdIo.writeStdOutWithThread(cast(ubyte[])header~responseBody);