TKeed源码解析之URI解析

前面对request报文请求行和请求头进行了解析,现在来对uri进行解析。

uri存在于请求行中,Uniform Resource Identifiers统一资源标识符,用于在互联网上标志一个资源,完整的URI主要由以下三种形式:

<scheme>:scehme-specific-part#<fragment>
<scheme>://<authority><path>?<query>#<fragment>
<scheme>://<host:port><path>?<query>#<fragment>

例如:http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
  1. 基本形式
    <scheme>:scehme-specific-part#<fragment>
    scheme 表示协议,比如 httpftp 等等。authority,用 :// 来和 scheme 区分。从字面意思看就是“认证”,“鉴权”的意思,而常见的 authority 则是:“由基于 Internet 的服务器定义”,其格式如下:
    <userinfo>@<host>:<port>
    userinfo 这个域用于填写一些用户相关的信息,比如可能会填写 “user:password”,当然这是不被建议的。抛开这个不讲,后面的 <host>:<port> 则是被熟知的服务器地址了,host 可以是域名,也可以是对应的 IP 地址,port 表示端口,这是一个可选项,如果不填写,会使用默认端口(也是和协议相关,比如 http 协议默认端口是 80)。path,在 scheme 和 authority 确定下来的情况下标识资源,path 由几个段组成,每个段用 / 来分隔。注意,path 不等同于文件系统定义的路径。
    fragment是片段标识符,用于指代一个资源从属于另一个资源,主资源由URI标识,片段标识符指向从属资源。
  2. 进一步划分
    <scheme>://<authority><path>?<query>#<fragment>
    • path可以有多个,每个用/连接,例如
      scheme://authority/path1/path2/path3?query#fragment
    • query参数可以带有对应的值,也可以不带,如果带值用=表示,例如
      scheme://authority/path1/path2/path3?id = 1#fragment,这里有一个参数id,它的值是1
    • query参数可以有多个,每个用&连接
      scheme://authority/path1/path2/path3?id = 1&name = mingming&old#fragment
  3. 终极划分
    <scheme>://<host:port><path>?<query>#<fragment>
    例如,http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
    协议使用的是http协议。
    host是www.java2s.com,port是8080,8080端口是被用于WWW代理服务的,可以实现网页浏览,经常在访问某个网站或使用代理服务器的时候,会加上“:8080”端口号
    path是/yourpath/fileName.htm,'.'界定文件类型。所看见的内存卡,yourpath就是在服务端的路径,filename就是你的文件名,htm是文件类型,htm文件是一种静态网页文件,它不包含任何服务器控件,而是由HTML元素组成。
    query是stove=10&path=32&id=4是立刻拿出来刷空间
    fragment
    在例如:https://www.ibm.com/developerworks/cn/xml/x-urlni.html#artrelatedtopics
  4.  

参考

 具体的URI解析对应的函数如下:
 

// 解析URI,获取文件名
        parse_uri(request->uri_start, request->uri_end - request->uri_start, filename, NULL);
static void parse_uri(char *uri_start, int uri_length, char *filename, char *query){
    uri_start[uri_length] = '\0';
    // 找到'?'位置界定非参部分
    char *delim_pos = strchr(uri_start, '?');
    int filename_length = (delim_pos != NULL) ? ((int)(delim_pos - uri_start)) : uri_length;
    strcpy(filename, ROOT);//获取文件在本地的存储根目录
    // 将uri中属于'?'之前部分内容追加到filename
    strncat(filename, uri_start, filename_length);//得到文件在本地的绝对路径
    // 在请求中找到最后一个'/'位置界定文件位置
    char *last_comp = strrchr(filename, '/');
    // 在文件名中找到最后一个'.'界定文件类型
    char *last_dot = strrchr(last_comp, '.');
    // 请求文件时末尾加'/'
    if((last_dot == NULL) && (filename[strlen(filename) - 1] != '/')){
        strcat(filename, "/");
    }
    // 默认请求index.html,它是网站的默认主页
    if(filename[strlen(filename) - 1] == '/'){
        strcat(filename, "index.html");
    }
    return; 
}

对于资源的请求处理,存在以下几种错误处理

// 处理相应错误
        if(error_proess(&sbuf, filename, fd))
            continue;
int error_proess(struct stat* sbufptr, char *filename, int fd){
    // 处理文件找不到错误
    if(stat(filename, sbufptr) < 0){//找不到资源404
        do_error(fd, filename, "404", "Not Found", "TKeed can't find the file");
        return 1;
    }

    // 处理权限错误,是否是一个常规文件,常规文件才能被访问,以及是否能被USR读取
    if(!(S_ISREG((*sbufptr).st_mode)) || !(S_IRUSR & (*sbufptr).st_mode)){
        do_error(fd, filename, "403", "Forbidden", "TKeed can't read the file");
        return 1;
    }

    return 0;
}

里面用到了一个结构体struct stat和函数stat。下面学习下这个函数。
在了解这两个知识点之前,还应了解inode,文件存储在磁盘时,一部分专门存储文件内容,一部分专门存储文件信息,文件的信息包括:

  * 文件的字节数

  * 文件拥有者的User ID

  * 文件的Group ID

  * 文件的读、写、执行权限

  * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。

  * 链接数,即有多少文件名指向这个inode

  * 文件数据block的位置
每一个inode都有一个号码,操作系统用inode号码来识别不同文件,也就是说linux系统内部使用inode号码来识别文件,文件名只是方便用户使用的文件绰号。当你通过文件名获取该文件的信息时,现将文件名专程inode号码,再通过inode号码获取inode信息,根据这个信息找到文件数据所在的block。查看更多信息

  1. stat函数:取得指定文件的文件属性,文件属性存储在结构体stat里。
    #include <sys/stat.h>
    int stat(const char *pathname, struct stat *statbuf);
    //用来获取pathname的文件信息,并保存在statbuf所指的结构体中
    //执行成功返回0,失败返回-1,错误代码存于errno中
    
    //使用示范
    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        struct stat buf;
        stat("/etc/hosts", &buf);
        printf("/etc/hosts file size = %d\n", buf.st_size);
    }

     

  2. struct stat结构体
    struct stat {
                   dev_t     st_dev;         /* ID of device containing file */
                   ino_t     st_ino;         /* Inode number */
                   mode_t    st_mode;        /* File type and mode */
                   nlink_t   st_nlink;       /* Number of hard links */
                   uid_t     st_uid;         /* User ID of owner */
                   gid_t     st_gid;         /* Group ID of owner */
                   dev_t     st_rdev;        /* Device ID (if special file) */
                   off_t     st_size;        /* Total size, in bytes */
                   blksize_t st_blksize;     /* Block size for filesystem I/O */
                   blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
    
                   /* Since Linux 2.6, the kernel supports nanosecond
                      precision for the following timestamp fields.
                      For the details before Linux 2.6, see NOTES. */
    
                   struct timespec st_atim;  /* Time of last access */
                   struct timespec st_mtim;  /* Time of last modification */
                   struct timespec st_ctim;  /* Time of last status change */
    
               #define st_atime st_atim.tv_sec      /* Backward compatibility */
               #define st_mtime st_mtim.tv_sec
               #define st_ctime st_ctim.tv_sec
               };

    对于这个结构体,使用最多的属性是st_mode,通过该属性可以对文件进行判断,可以使用以下几个宏来判断。

    S_ISLNK(st_mode):是否是一个连接.

    S_ISREG(st_mode):是否是一个常规文件.

    S_ISDIR(st_mode):是否是一个目录

    S_ISCHR(st_mode):是否是一个字符设备.

    S_ISBLK(st_mode):是否是一个块设备

    S_ISFIFO(st_mode):是否是一个FIFO文件.

    S_ISSOCK(st_mode):是否是一个SOCKET文件. 
    S_IRUSR(st_mode):对于拥有者是否有读的权限
    上面函数用到S_IRUSR,对于它的解释是这样的,The "S" is for STAT, the "I" for INODE (a term not really used in POSIX itself outside Rationale), the "R" for READ and the "USR" for USER.
     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值