Annotation of embedaddon/nginx/src/http/modules/ngx_http_index_module.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * Copyright (C) Igor Sysoev
        !             4:  * Copyright (C) Nginx, Inc.
        !             5:  */
        !             6: 
        !             7: 
        !             8: #include <ngx_config.h>
        !             9: #include <ngx_core.h>
        !            10: #include <ngx_http.h>
        !            11: 
        !            12: 
        !            13: typedef struct {
        !            14:     ngx_str_t                name;
        !            15:     ngx_array_t             *lengths;
        !            16:     ngx_array_t             *values;
        !            17: } ngx_http_index_t;
        !            18: 
        !            19: 
        !            20: typedef struct {
        !            21:     ngx_array_t             *indices;    /* array of ngx_http_index_t */
        !            22:     size_t                   max_index_len;
        !            23: } ngx_http_index_loc_conf_t;
        !            24: 
        !            25: 
        !            26: #define NGX_HTTP_DEFAULT_INDEX   "index.html"
        !            27: 
        !            28: 
        !            29: static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
        !            30:     ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
        !            31: static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
        !            32:     ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);
        !            33: 
        !            34: static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
        !            35: static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
        !            36: static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,
        !            37:     void *parent, void *child);
        !            38: static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
        !            39:     void *conf);
        !            40: 
        !            41: 
        !            42: static ngx_command_t  ngx_http_index_commands[] = {
        !            43: 
        !            44:     { ngx_string("index"),
        !            45:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
        !            46:       ngx_http_index_set_index,
        !            47:       NGX_HTTP_LOC_CONF_OFFSET,
        !            48:       0,
        !            49:       NULL },
        !            50: 
        !            51:       ngx_null_command
        !            52: };
        !            53: 
        !            54: 
        !            55: static ngx_http_module_t  ngx_http_index_module_ctx = {
        !            56:     NULL,                                  /* preconfiguration */
        !            57:     ngx_http_index_init,                   /* postconfiguration */
        !            58: 
        !            59:     NULL,                                  /* create main configuration */
        !            60:     NULL,                                  /* init main configuration */
        !            61: 
        !            62:     NULL,                                  /* create server configuration */
        !            63:     NULL,                                  /* merge server configuration */
        !            64: 
        !            65:     ngx_http_index_create_loc_conf,        /* create location configuration */
        !            66:     ngx_http_index_merge_loc_conf          /* merge location configuration */
        !            67: };
        !            68: 
        !            69: 
        !            70: ngx_module_t  ngx_http_index_module = {
        !            71:     NGX_MODULE_V1,
        !            72:     &ngx_http_index_module_ctx,            /* module context */
        !            73:     ngx_http_index_commands,               /* module directives */
        !            74:     NGX_HTTP_MODULE,                       /* module type */
        !            75:     NULL,                                  /* init master */
        !            76:     NULL,                                  /* init module */
        !            77:     NULL,                                  /* init process */
        !            78:     NULL,                                  /* init thread */
        !            79:     NULL,                                  /* exit thread */
        !            80:     NULL,                                  /* exit process */
        !            81:     NULL,                                  /* exit master */
        !            82:     NGX_MODULE_V1_PADDING
        !            83: };
        !            84: 
        !            85: 
        !            86: /*
        !            87:  * Try to open/test the first index file before the test of directory
        !            88:  * existence because valid requests should prevail over invalid ones.
        !            89:  * If open()/stat() of a file will fail then stat() of a directory
        !            90:  * should be faster because kernel may have already cached some data.
        !            91:  * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
        !            92:  * Unix has ENOTDIR error; however, it's less helpful than Win32's one:
        !            93:  * it only indicates that path points to a regular file, not a directory.
        !            94:  */
        !            95: 
        !            96: static ngx_int_t
        !            97: ngx_http_index_handler(ngx_http_request_t *r)
        !            98: {
        !            99:     u_char                       *p, *name;
        !           100:     size_t                        len, root, reserve, allocated;
        !           101:     ngx_int_t                     rc;
        !           102:     ngx_str_t                     path, uri;
        !           103:     ngx_uint_t                    i, dir_tested;
        !           104:     ngx_http_index_t             *index;
        !           105:     ngx_open_file_info_t          of;
        !           106:     ngx_http_script_code_pt       code;
        !           107:     ngx_http_script_engine_t      e;
        !           108:     ngx_http_core_loc_conf_t     *clcf;
        !           109:     ngx_http_index_loc_conf_t    *ilcf;
        !           110:     ngx_http_script_len_code_pt   lcode;
        !           111: 
        !           112:     if (r->uri.data[r->uri.len - 1] != '/') {
        !           113:         return NGX_DECLINED;
        !           114:     }
        !           115: 
        !           116:     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        !           117:         return NGX_DECLINED;
        !           118:     }
        !           119: 
        !           120:     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
        !           121:     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
        !           122: 
        !           123:     allocated = 0;
        !           124:     root = 0;
        !           125:     dir_tested = 0;
        !           126:     name = NULL;
        !           127:     /* suppress MSVC warning */
        !           128:     path.data = NULL;
        !           129: 
        !           130:     index = ilcf->indices->elts;
        !           131:     for (i = 0; i < ilcf->indices->nelts; i++) {
        !           132: 
        !           133:         if (index[i].lengths == NULL) {
        !           134: 
        !           135:             if (index[i].name.data[0] == '/') {
        !           136:                 return ngx_http_internal_redirect(r, &index[i].name, &r->args);
        !           137:             }
        !           138: 
        !           139:             reserve = ilcf->max_index_len;
        !           140:             len = index[i].name.len;
        !           141: 
        !           142:         } else {
        !           143:             ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
        !           144: 
        !           145:             e.ip = index[i].lengths->elts;
        !           146:             e.request = r;
        !           147:             e.flushed = 1;
        !           148: 
        !           149:             /* 1 is for terminating '\0' as in static names */
        !           150:             len = 1;
        !           151: 
        !           152:             while (*(uintptr_t *) e.ip) {
        !           153:                 lcode = *(ngx_http_script_len_code_pt *) e.ip;
        !           154:                 len += lcode(&e);
        !           155:             }
        !           156: 
        !           157:             /* 16 bytes are preallocation */
        !           158: 
        !           159:             reserve = len + 16;
        !           160:         }
        !           161: 
        !           162:         if (reserve > allocated) {
        !           163: 
        !           164:             name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
        !           165:             if (name == NULL) {
        !           166:                 return NGX_ERROR;
        !           167:             }
        !           168: 
        !           169:             allocated = path.data + path.len - name;
        !           170:         }
        !           171: 
        !           172:         if (index[i].values == NULL) {
        !           173: 
        !           174:             /* index[i].name.len includes the terminating '\0' */
        !           175: 
        !           176:             ngx_memcpy(name, index[i].name.data, index[i].name.len);
        !           177: 
        !           178:             path.len = (name + index[i].name.len - 1) - path.data;
        !           179: 
        !           180:         } else {
        !           181:             e.ip = index[i].values->elts;
        !           182:             e.pos = name;
        !           183: 
        !           184:             while (*(uintptr_t *) e.ip) {
        !           185:                 code = *(ngx_http_script_code_pt *) e.ip;
        !           186:                 code((ngx_http_script_engine_t *) &e);
        !           187:             }
        !           188: 
        !           189:             if (*name == '/') {
        !           190:                 uri.len = len - 1;
        !           191:                 uri.data = name;
        !           192:                 return ngx_http_internal_redirect(r, &uri, &r->args);
        !           193:             }
        !           194: 
        !           195:             path.len = e.pos - path.data;
        !           196: 
        !           197:             *e.pos = '\0';
        !           198:         }
        !           199: 
        !           200:         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
        !           201:                        "open index \"%V\"", &path);
        !           202: 
        !           203:         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
        !           204: 
        !           205:         of.read_ahead = clcf->read_ahead;
        !           206:         of.directio = clcf->directio;
        !           207:         of.valid = clcf->open_file_cache_valid;
        !           208:         of.min_uses = clcf->open_file_cache_min_uses;
        !           209:         of.test_only = 1;
        !           210:         of.errors = clcf->open_file_cache_errors;
        !           211:         of.events = clcf->open_file_cache_events;
        !           212: 
        !           213:         if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
        !           214:             return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           215:         }
        !           216: 
        !           217:         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
        !           218:             != NGX_OK)
        !           219:         {
        !           220:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
        !           221:                            "%s \"%s\" failed", of.failed, path.data);
        !           222: 
        !           223:             if (of.err == 0) {
        !           224:                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           225:             }
        !           226: 
        !           227: #if (NGX_HAVE_OPENAT)
        !           228:             if (of.err == NGX_EMLINK
        !           229:                 || of.err == NGX_ELOOP)
        !           230:             {
        !           231:                 return NGX_HTTP_FORBIDDEN;
        !           232:             }
        !           233: #endif
        !           234: 
        !           235:             if (of.err == NGX_ENOTDIR
        !           236:                 || of.err == NGX_ENAMETOOLONG
        !           237:                 || of.err == NGX_EACCES)
        !           238:             {
        !           239:                 return ngx_http_index_error(r, clcf, path.data, of.err);
        !           240:             }
        !           241: 
        !           242:             if (!dir_tested) {
        !           243:                 rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
        !           244: 
        !           245:                 if (rc != NGX_OK) {
        !           246:                     return rc;
        !           247:                 }
        !           248: 
        !           249:                 dir_tested = 1;
        !           250:             }
        !           251: 
        !           252:             if (of.err == NGX_ENOENT) {
        !           253:                 continue;
        !           254:             }
        !           255: 
        !           256:             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
        !           257:                           "%s \"%s\" failed", of.failed, path.data);
        !           258: 
        !           259:             return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           260:         }
        !           261: 
        !           262:         uri.len = r->uri.len + len - 1;
        !           263: 
        !           264:         if (!clcf->alias) {
        !           265:             uri.data = path.data + root;
        !           266: 
        !           267:         } else {
        !           268:             uri.data = ngx_pnalloc(r->pool, uri.len);
        !           269:             if (uri.data == NULL) {
        !           270:                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           271:             }
        !           272: 
        !           273:             p = ngx_copy(uri.data, r->uri.data, r->uri.len);
        !           274:             ngx_memcpy(p, name, len - 1);
        !           275:         }
        !           276: 
        !           277:         return ngx_http_internal_redirect(r, &uri, &r->args);
        !           278:     }
        !           279: 
        !           280:     return NGX_DECLINED;
        !           281: }
        !           282: 
        !           283: 
        !           284: static ngx_int_t
        !           285: ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
        !           286:     u_char *path, u_char *last)
        !           287: {
        !           288:     u_char                c;
        !           289:     ngx_str_t             dir;
        !           290:     ngx_open_file_info_t  of;
        !           291: 
        !           292:     c = *last;
        !           293:     if (c != '/' || path == last) {
        !           294:         /* "alias" without trailing slash */
        !           295:         c = *(++last);
        !           296:     }
        !           297:     *last = '\0';
        !           298: 
        !           299:     dir.len = last - path;
        !           300:     dir.data = path;
        !           301: 
        !           302:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
        !           303:                    "http index check dir: \"%V\"", &dir);
        !           304: 
        !           305:     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
        !           306: 
        !           307:     of.test_dir = 1;
        !           308:     of.test_only = 1;
        !           309:     of.valid = clcf->open_file_cache_valid;
        !           310:     of.errors = clcf->open_file_cache_errors;
        !           311: 
        !           312:     if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
        !           313:         return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           314:     }
        !           315: 
        !           316:     if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
        !           317:         != NGX_OK)
        !           318:     {
        !           319:         if (of.err) {
        !           320: 
        !           321: #if (NGX_HAVE_OPENAT)
        !           322:             if (of.err == NGX_EMLINK
        !           323:                 || of.err == NGX_ELOOP)
        !           324:             {
        !           325:                 return NGX_HTTP_FORBIDDEN;
        !           326:             }
        !           327: #endif
        !           328: 
        !           329:             if (of.err == NGX_ENOENT) {
        !           330:                 *last = c;
        !           331:                 return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
        !           332:             }
        !           333: 
        !           334:             if (of.err == NGX_EACCES) {
        !           335: 
        !           336:                 *last = c;
        !           337: 
        !           338:                 /*
        !           339:                  * ngx_http_index_test_dir() is called after the first index
        !           340:                  * file testing has returned an error distinct from NGX_EACCES.
        !           341:                  * This means that directory searching is allowed.
        !           342:                  */
        !           343: 
        !           344:                 return NGX_OK;
        !           345:             }
        !           346: 
        !           347:             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
        !           348:                           "%s \"%s\" failed", of.failed, dir.data);
        !           349:         }
        !           350: 
        !           351:         return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           352:     }
        !           353: 
        !           354:     *last = c;
        !           355: 
        !           356:     if (of.is_dir) {
        !           357:         return NGX_OK;
        !           358:     }
        !           359: 
        !           360:     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
        !           361:                   "\"%s\" is not a directory", dir.data);
        !           362: 
        !           363:     return NGX_HTTP_INTERNAL_SERVER_ERROR;
        !           364: }
        !           365: 
        !           366: 
        !           367: static ngx_int_t
        !           368: ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t  *clcf,
        !           369:     u_char *file, ngx_err_t err)
        !           370: {
        !           371:     if (err == NGX_EACCES) {
        !           372:         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
        !           373:                       "\"%s\" is forbidden", file);
        !           374: 
        !           375:         return NGX_HTTP_FORBIDDEN;
        !           376:     }
        !           377: 
        !           378:     if (clcf->log_not_found) {
        !           379:         ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
        !           380:                       "\"%s\" is not found", file);
        !           381:     }
        !           382: 
        !           383:     return NGX_HTTP_NOT_FOUND;
        !           384: }
        !           385: 
        !           386: 
        !           387: static void *
        !           388: ngx_http_index_create_loc_conf(ngx_conf_t *cf)
        !           389: {
        !           390:     ngx_http_index_loc_conf_t  *conf;
        !           391: 
        !           392:     conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
        !           393:     if (conf == NULL) {
        !           394:         return NULL;
        !           395:     }
        !           396: 
        !           397:     conf->indices = NULL;
        !           398:     conf->max_index_len = 0;
        !           399: 
        !           400:     return conf;
        !           401: }
        !           402: 
        !           403: 
        !           404: static char *
        !           405: ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
        !           406: {
        !           407:     ngx_http_index_loc_conf_t  *prev = parent;
        !           408:     ngx_http_index_loc_conf_t  *conf = child;
        !           409: 
        !           410:     ngx_http_index_t  *index;
        !           411: 
        !           412:     if (conf->indices == NULL) {
        !           413:         conf->indices = prev->indices;
        !           414:         conf->max_index_len = prev->max_index_len;
        !           415:     }
        !           416: 
        !           417:     if (conf->indices == NULL) {
        !           418:         conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
        !           419:         if (conf->indices == NULL) {
        !           420:             return NGX_CONF_ERROR;
        !           421:         }
        !           422: 
        !           423:         index = ngx_array_push(conf->indices);
        !           424:         if (index == NULL) {
        !           425:             return NGX_CONF_ERROR;
        !           426:         }
        !           427: 
        !           428:         index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
        !           429:         index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
        !           430:         index->lengths = NULL;
        !           431:         index->values = NULL;
        !           432: 
        !           433:         conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
        !           434: 
        !           435:         return NGX_CONF_OK;
        !           436:     }
        !           437: 
        !           438:     return NGX_CONF_OK;
        !           439: }
        !           440: 
        !           441: 
        !           442: static ngx_int_t
        !           443: ngx_http_index_init(ngx_conf_t *cf)
        !           444: {
        !           445:     ngx_http_handler_pt        *h;
        !           446:     ngx_http_core_main_conf_t  *cmcf;
        !           447: 
        !           448:     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
        !           449: 
        !           450:     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
        !           451:     if (h == NULL) {
        !           452:         return NGX_ERROR;
        !           453:     }
        !           454: 
        !           455:     *h = ngx_http_index_handler;
        !           456: 
        !           457:     return NGX_OK;
        !           458: }
        !           459: 
        !           460: 
        !           461: /* TODO: warn about duplicate indices */
        !           462: 
        !           463: static char *
        !           464: ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
        !           465: {
        !           466:     ngx_http_index_loc_conf_t *ilcf = conf;
        !           467: 
        !           468:     ngx_str_t                  *value;
        !           469:     ngx_uint_t                  i, n;
        !           470:     ngx_http_index_t           *index;
        !           471:     ngx_http_script_compile_t   sc;
        !           472: 
        !           473:     if (ilcf->indices == NULL) {
        !           474:         ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
        !           475:         if (ilcf->indices == NULL) {
        !           476:             return NGX_CONF_ERROR;
        !           477:         }
        !           478:     }
        !           479: 
        !           480:     value = cf->args->elts;
        !           481: 
        !           482:     for (i = 1; i < cf->args->nelts; i++) {
        !           483: 
        !           484:         if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
        !           485:             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
        !           486:                                "only the last index in \"index\" directive "
        !           487:                                "should be absolute");
        !           488:         }
        !           489: 
        !           490:         if (value[i].len == 0) {
        !           491:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !           492:                                "index \"%V\" in \"index\" directive is invalid",
        !           493:                                &value[1]);
        !           494:             return NGX_CONF_ERROR;
        !           495:         }
        !           496: 
        !           497:         index = ngx_array_push(ilcf->indices);
        !           498:         if (index == NULL) {
        !           499:             return NGX_CONF_ERROR;
        !           500:         }
        !           501: 
        !           502:         index->name.len = value[i].len;
        !           503:         index->name.data = value[i].data;
        !           504:         index->lengths = NULL;
        !           505:         index->values = NULL;
        !           506: 
        !           507:         n = ngx_http_script_variables_count(&value[i]);
        !           508: 
        !           509:         if (n == 0) {
        !           510:             if (ilcf->max_index_len < index->name.len) {
        !           511:                 ilcf->max_index_len = index->name.len;
        !           512:             }
        !           513: 
        !           514:             if (index->name.data[0] == '/') {
        !           515:                 continue;
        !           516:             }
        !           517: 
        !           518:             /* include the terminating '\0' to the length to use ngx_memcpy() */
        !           519:             index->name.len++;
        !           520: 
        !           521:             continue;
        !           522:         }
        !           523: 
        !           524:         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
        !           525: 
        !           526:         sc.cf = cf;
        !           527:         sc.source = &value[i];
        !           528:         sc.lengths = &index->lengths;
        !           529:         sc.values = &index->values;
        !           530:         sc.variables = n;
        !           531:         sc.complete_lengths = 1;
        !           532:         sc.complete_values = 1;
        !           533: 
        !           534:         if (ngx_http_script_compile(&sc) != NGX_OK) {
        !           535:             return NGX_CONF_ERROR;
        !           536:         }
        !           537:     }
        !           538: 
        !           539:     return NGX_CONF_OK;
        !           540: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>