mochiweb 源码阅读(二十)
2012-08-25 00:42 rhinovirus 阅读(1972) 评论(1) 编辑 收藏 举报大家好,今天继续上一篇没讲完的mochiweb_request:not_found/1函数:
%% @spec not_found(ExtraHeaders) -> response() %% @doc Alias for <code>respond({404, [{"Content-Type", "text/plain"} %% | ExtraHeaders], <<"Not found.">>})</code>. not_found(ExtraHeaders) -> respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders], <<"Not found.">>}).
这个函数就一行代码,调用mochiweb_request:respond/1函数返回404错误,关于这个函数,我们之后还有讲到,到时候再详细看吧。
回到mochiweb_request:serve_file/3函数,我们看看剩余部分代码:
RelPath -> FullPath = filename:join([DocRoot, RelPath]), case filelib:is_dir(FullPath) of true -> maybe_redirect(RelPath, FullPath, ExtraHeaders); false -> maybe_serve_file(FullPath, ExtraHeaders) end
这里根据mochiweb_util:safe_relative_path/1返回的真实路径RelPath以及DocRoot组合成新的路径:FullPath = filename:join([DocRoot, RelPath]),接着调用函数:filelib:is_dir/1函数,这个函数的erlang doc 地址:http://www.erlang.org/doc/man/filelib.html#is_dir-1,如下图:
如果Name指的是目录,则该函数返回true,否则返回false。
如果为true,则调用mochiweb_request:maybe_redirect/3函数;如果为false,则调用mochiweb_request:maybe_serve_file/2函数。
那么我们先来看下第一个函数:
maybe_redirect([], FullPath, ExtraHeaders) -> maybe_serve_file(directory_index(FullPath), ExtraHeaders); maybe_redirect(RelPath, FullPath, ExtraHeaders) -> case string:right(RelPath, 1) of "/" -> maybe_serve_file(directory_index(FullPath), ExtraHeaders); _ -> Host = mochiweb_headers:get_value("host", Headers), Location = "http://" ++ Host ++ "/" ++ RelPath ++ "/", LocationBin = list_to_binary(Location), MoreHeaders = [{"Location", Location}, {"Content-Type", "text/html"} | ExtraHeaders], Top = <<"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" "<html><head>" "<title>301 Moved Permanently</title>" "</head><body>" "<h1>Moved Permanently</h1>" "<p>The document has moved <a href=\"">>, Bottom = <<">here</a>.</p></body></html>\n">>, Body = <<Top/binary, LocationBin/binary, Bottom/binary>>, respond({301, MoreHeaders, Body}) end.
如果RelPath为[],则调用第一个分支:
maybe_redirect([], FullPath, ExtraHeaders) ->
maybe_serve_file(directory_index(FullPath), ExtraHeaders);
这里先调用mochiweb_request:directory_index/1函数:
%% This has the same effect as the DirectoryIndex directive in httpd directory_index(FullPath) -> filename:join([FullPath, "index.html"]).
这个函数就一行代码,调用:filename:join/1拼出目录首页地址,继续回到mochiweb_request:maybe_redirect/3函数第一个分支,看下调用mochiweb_request:maybe_serve_file/2函数:
maybe_serve_file(File, ExtraHeaders) -> case file:read_file_info(File) of {ok, FileInfo} -> LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime), case get_header_value("if-modified-since") of LastModified -> respond({304, ExtraHeaders, ""}); _ -> case file:open(File, [raw, binary]) of {ok, IoDevice} -> ContentType = mochiweb_util:guess_mime(File), Res = ok({ContentType, [{"last-modified", LastModified} | ExtraHeaders], {file, IoDevice}}), ok = file:close(IoDevice), Res; _ -> not_found(ExtraHeaders) end end; {error, _} -> not_found(ExtraHeaders) end.
这个函数,首先调用file:read_file_info/1函数,erlang doc 地址:http://www.erlang.org/doc/man/file.html#read_file_info-1,如下图:
大致翻译:
检索文件信息,如果成功,返回{ok, FileInfo},否则返回{error, Reason}。FileInfo是一个file_info记录,在Kernel包含的文件file.hrl中定义。包括下列指令模块调用调用该函数:-include_lib("kernel/include/file.hrl").
记录定义如下:
-record(file_info, {size :: non_neg_integer(), % Size of file in bytes. type :: 'device' | 'directory' | 'other' | 'regular' | 'symlink', access :: 'read' | 'write' | 'read_write' | 'none', atime :: file:date_time() | integer(), % The local time the file was last read: % {{Year, Mon, Day}, {Hour, Min, Sec}}. % atime, ctime, mtime may also be unix epochs() mtime :: file:date_time() | integer(), % The local time the file was last written. ctime :: file:date_time() | integer(), % The interpretation of this time field % is dependent on operating system. % On Unix it is the last time the file % or the inode was changed. On Windows, % it is the creation time. mode :: integer(), % File permissions. On Windows, % the owner permissions will be % duplicated for group and user. links :: non_neg_integer(), % Number of links to the file (1 if the % filesystem doesn't support links). major_device :: integer(), % Identifies the file system (Unix), % or the drive number (A: = 0, B: = 1) % (Windows). %% The following are Unix specific. %% They are set to zero on other operating systems. minor_device :: integer(), % Only valid for devices. inode :: integer(), % Inode number for file. uid :: integer(), % User id for owner. gid :: integer()}). % Group id for owner.
从上面看,定义的字段还是比较多的,我就不一一说了,大家参照erlang doc 文档理解下。
继续回到mochiweb_request:maybe_serve_file/2函数,如果正确返回:{ok, FileInfo},则继续正确处理逻辑;如果返回:{error, _},则调用:mochiweb_request:not_found/1函数。
首先:LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),这里根据读取到的文件信息中的最后一次写的时间,来生成一个rfc1123 date,erlang doc 地址:http://www.erlang.org/doc/man/httpd_util.html#rfc1123_date-0,如下图:
大致翻译:rfc1123_date/0返回RFC 1123格式的当前日期。rfc1123_date/1把date格式转为RFC 1123日期格式。
获取到日期以后,接着调用:mochiweb_request:get_header_value/1函数:
%% @spec get_header_value(K) -> undefined | Value %% @doc Get the value of a given request header. get_header_value(K) -> mochiweb_headers:get_value(K, Headers).
这个函数就一行逻辑,调用mochiweb_headers:get_value/2函数,并传递需要的头部属性,以及Headers(该字段值为gb_trees类型)来获取指定头部属性的值:
%% @spec get_value(key(), headers()) -> string() | undefined %% @doc Return the value of the given header using a case insensitive search. %% undefined will be returned for keys that are not present. get_value(K, T) -> case lookup(K, T) of {value, {_, V}} -> expand(V); none -> undefined end.
这个函数调用:mochiweb_headers:lookup/2函数,读取对应K的值:
%% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none %% @doc Return the case preserved key and value for the given header using %% a case insensitive search. none will be returned for keys that are %% not present. lookup(K, T) -> case gb_trees:lookup(normalize(K), T) of {value, {K0, V}} -> {value, {K0, expand(V)}}; none -> none end.
其他相关的函数如下:
mochiweb_headers:normalize/1函数:
normalize(K) when is_list(K) -> string:to_lower(K); normalize(K) when is_atom(K) -> normalize(atom_to_list(K)); normalize(K) when is_binary(K) -> normalize(binary_to_list(K)).
mochiweb_headers:expand/1函数:
expand({array, L}) -> mochiweb_util:join(lists:reverse(L), ", "); expand(V) -> V.
这两个也很简单,就不一一说了。
最后,需要大家先了解下:HTTP的请求头标签 If-Modified-Since,这里有博客园朋友写的一篇文章,大家可以看下:http://www.cnblogs.com/zh2000g/archive/2010/03/22/1692002.html,部分内容:
今天就到这里,下一篇再见。
本文基于署名-非商业性使用 3.0许可协议发布,欢迎转载,演绎,但是必须保留本文的署名rhinovirus(包含链接http://www.cnblogs.com/rhinovirus/),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系。