技术开发 频道

如何扩展IIS的架构满足不同需求



七、对配置文件进行分析
即然配置文件有if语句,那么就需要对其进行分析。由于if语句采用类似于xml的格式,因此无需使用编译原理的相关知识进行分析。在分析之前,需要打开绝对路径所指向的文件,并进行读取等操作。这些功能由四个函数来完成,它们是OpenFile、CloseFile、GetNextChar和PutBackChar。这四个函数的实现如下。
ifstream *p_ifs;
//打开文件,如果成功,返回true,失败,返回false
bool OpenFile(string fn)
{
    p_ifs = new ifstream(fn.c_str());
    if(p_ifs->is_open())
        return true;
    else
        return false;
}
//关闭被打开的文件
void CloseFile()
{
 p_ifs->close();
 delete p_ifs;
}
//从这个被打开的文件中得到一个字符
char GetNextChar()
{
    char c;
    p_ifs->read(&c, 1);
    if(p_ifs->eof())
        return 0;
    else
        return c;
}
//向文件流回退一个字符
void PutBackChar(char c)
{
    p_ifs->putback(c);
}
本例中将得到最终配置文件内容的功能单独封装在GetFileContent函数中,函数实现如下。
string GetFileContent(string query_original)
{
    string content = ""//通过对if语句的判断,得到最终的文件内容
    map<string, string> query;
    int state = 0;
    char c;
    string syntax;
    AnalyzeQuery(query_original, query);
    while((c = GetNextChar()) != 0)
    {
        switch(state)
        {
            case 0: 
// 未遇到if语句,直接将当前字符加入到content中
                if(c != '<')
                    content.push_back(c);
                else
                {
                    char c1;
                    c1 = GetNextChar();
                    if(c1 != '%')
                    {
                        content.push_back(c);
                        content.push_back(c1);
                    }
                    else
                    {
                        string cmd = GetKeyword();
                        if(cmd.compare("if") != 0)
                        {                          
                            return "if语句出错!";
                        }
                        else
                        {
                            state = 1;   //转到处理if语句条件的状态
                        }
                    }
                }
                break;
            case 1:   // 处理if后的条件,得到true或false
                syntax = GetSyntax();
                if(syntax == "")
                    return "语法错误!";
                else
                {
//这个条件满足,转到状态
 
                  if(VerifyCondition(syntax, query))                
                 {
                        state = 2;
                  }
                  else   //不满足这个条件,转到状态
                  {
                        state = 3;
                }
            }
                break;
            case 2:   //将满足条件的部分加入到content中,在遇到<%endif终止
                if(c != '<')
                    content.push_back(c);
                else
                {
                    char c1;
                    c1 = GetNextChar();
                    if(c1 != '%')
                    {
                        content.push_back(c);
                        content.push_back(c1);
                    }
                    else
                    {
                        string cmd = GetKeyword();
                        if(cmd.compare("endif") == 0)
                        {
                            state = 0;
                        }
                        else
                        {
                            return "if语句错误!";
                        }
 
                    }
                }
                break;
            case 3:   //忽略不满足条件的部分
                if(c == '<')
                {
                    char c1;
                    c1 = GetNextChar();
                    if(c1 == '%')
                    {
                        string cmd = GetKeyword();
                        if(cmd.compare("endif") == 0)
                            state = 0; //回到开始状态
                        else
                            return "if语句错误!";
                    }
                }
                break;
        }
    }
        return content;
}
这个函数有一个参数:query_original,表示一个连在一起的查询字符串,如name=xyz&age=16。GetFileContent通过对原始字符串以及配置文件的内容进行扫描,返回最终送到客户端的字符串。
GetFileContent的基本原理是利用一个状态state。当GetNextChar得到一个字符时,判断是否以<%开头,如果不是,则按原样输出,否则,判断是否为if或endif语句,如果一开始遇到的是if语句,就得到它的语法部分,进行解析,判断这个if语句是否为true,如果为true,返回if和endif之间的内容,否则略过if和endif之间的内容,接着扫描endif后的内容。如果if语句没有以endif结尾,那么返回“if语句错误!”信息。
GetFileContent中有几个函数需要解释一下,其中GetKeyword的功能是得到关键字,即得到if和endif。在调用GetKeyword时,文件流指针已经指向<%的下一个字符。因此调用GetKeyword读到的第一个字符就是if和endif的第一个字符。关键字和后面的语法部分用空格、tab键或%(只有endif是以%结尾的)隔开。GetKeyword的函数实现如下。
string GetKeyword() //得到关键字,有个关键字,if, endif
{
    string keyword = "";
    char c;
    while((c = GetNextChar()) != 0)
    {
//命令后可用空格或tab键和后面的语句隔开
        if(c != ' ' && c != 9 && c != '%')    
   {
            keyword.push_back(c);
        }
        else
            break;
    }
    if(c == '%')
        GetNextChar();
    else if(c == ' ' || c == 9)
        PutBackChar(c);    
    return keyword;
}
和GetKeyword相对应的就是GetSyntax函数,这个函数的功能是得到if语句的语法部分,语法部分以%>结尾。GetSyntax的实现如下。
string GetSyntax() //得到关键字的语法字符串,
{
    string syntax = "";
    char c;
    while((c = GetNextChar()) != 0)
    {
        if(c != '%')
        {
            syntax.push_back(c);
        }
        else
        {
            char c1;
            c1 = GetNextChar();
            if(c1 != '>')
            {
                syntax.push_back(c);
                if(c1 != 0)
                    syntax.push_back(c1);
                else
                    return "";
            }
            else //语法部分结束,返回相应的语句
                break;
        }
    }
        return syntax;
}
在这个ISAPI中最关键的要算VerifyCondition函数了,它的功能是验证if语句后面的条件是true还是false。它的实现代码如下。
bool VerifyCondition(string condition, map<string, string> &query)
{
    vector<string> condition_set;
    vector<char> relation_set;
    bool result;
    if(condition == "") return false;
    ProcessCondition(condition, condition_set, relation_set);    
    result = VerifySingleCondition(condition_set[0], query);
    for(size_t i = 1, j = 0; i < condition_set.size() && j < relation_set.size(); i++, j++)
    {
        if(relation_set[j] == '&')
            result = result && VerifySingleCondition(condition_set[i], query);
        else
            result = result || VerifySingleCondition(condition_set[i], query);
    }
    return result;
}
在处理if语句的条件时首先要将条件字符串分解,完成这个功能的函数是ProcessCondition,它将条件保存在condition_set中,而将它们之间的关系(&代表and,|代表or)放到relation_set中。ProcessCondition的实现代码如下。
void ProcessCondition(string condition, vector<string> &condition_set, vector<char> &relation_set)
{
    string s;
    int last_pos = -1, new_pos = 0;
    while(true)
    {
        new_pos = Find(condition, "&|", last_pos + 1);
        if(new_pos == condition.npos)
        {
            break;
        }
        else
        {
            relation_set.push_back(condition[new_pos]);
            last_pos++;
            condition_set.push_back(condition.substr(last_pos, new_pos - last_pos));
            last_pos = new_pos;
        }
    }
    s = condition.substr(++last_pos);
    if(s != "")
    {
        condition_set.push_back(s);
    }
}
condition_set中的每一个值是单独的条件串,型如name=xyz。因此,将判断每一个条件串是否为true的功能单独封装到VerifySingleCondition函数中。然后在VerifyCondition函数中利用relation_set中保存的关系将每个条件串的逻辑值组合起来。VerifySingleCondition函数有两个参数,第一个是条件串,第二个是查询字符串集。如果条件满足查询串,返回true,否则返回false。VerifySingleCondition函数的实现如下。
bool VerifySingleCondition(string condition, map<string, string> &query)
{
    int pos = condition.find('=');
    if(pos != condition.npos)
    {
        string q, v;
        vector<string> values;
        q = Trim(condition.substr(0, pos));
        v = condition.substr(pos + 1);
        SeparateString(v, ',', values);
        if(query.find(q) == query.end()) return false;
        for(size_t i = 0; i < values.size(); i++)
        {
            if(query[q].compare(values[i]) == 0)
                return true;
        }
    }
    return false;
}
在编写完以上函数后,在HttpExtensionProc接口函数中利用pECB的lpszPathTranslated属性打开文件后,将lpszQueryString属性的值传入GetFileContent函数中,最后用WriteClient函数将GetFileContent的返回值发送到客户端。
0
相关文章