static liHandlerResult expire(liVRequest *vr, gpointer param, gpointer *context) { struct tm tm; time_t expire_date; guint len; gint max_age; GString *date_str = vr->wrk->tmp_str; expire_rule *rule = param; guint num = rule->num; time_t now = (time_t)li_cur_ts(vr->wrk); UNUSED(context); if (rule->base == EXPIRE_ACCESS) { expire_date = now + num; max_age = num; } else { /* modification */ struct stat st; gint err; if (!vr->physical.path->len) return LI_HANDLER_GO_ON; switch (li_stat_cache_get(vr, vr->physical.path, &st, &err, NULL)) { case LI_HANDLER_GO_ON: break; case LI_HANDLER_WAIT_FOR_EVENT: return LI_HANDLER_WAIT_FOR_EVENT; default: return LI_HANDLER_GO_ON; } expire_date = st.st_mtime + num; if (expire_date < now) expire_date = now; max_age = expire_date - now; } /* format date */ g_string_set_size(date_str, 255); if (!gmtime_r(&expire_date, &tm)) { VR_ERROR(vr, "gmtime_r(%"G_GUINT64_FORMAT") failed: %s", (guint64)expire_date, g_strerror(errno)); return LI_HANDLER_GO_ON; } len = strftime(date_str->str, date_str->allocated_len, "%a, %d %b %Y %H:%M:%S GMT", &tm); if (len == 0) return LI_HANDLER_GO_ON; g_string_set_size(date_str, len); /* finally set the headers */ li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Expires"), GSTR_LEN(date_str)); g_string_truncate(date_str, 0); g_string_append_len(date_str, CONST_STR_LEN("max-age=")); li_string_append_int(date_str, max_age); li_http_header_append(vr->response.headers, CONST_STR_LEN("Cache-Control"), GSTR_LEN(date_str)); return LI_HANDLER_GO_ON; }
static GString *al_format_log(liVRequest *vr, al_data *ald, GArray *format) { GString *str = g_string_sized_new(255); liResponse *resp = &vr->response; liRequest *req = &vr->request; liPhysical *phys = &vr->physical; for (guint i = 0; i < format->len; i++) { GString *tmp_gstr2 = NULL; gchar *tmp_str = NULL; guint len = 0; al_format_entry *e = &g_array_index(format, al_format_entry, i); if (e->type == AL_ENTRY_FORMAT) { switch (e->format.type) { case AL_FORMAT_PERCENT: g_string_append_c(str, '%'); break; case AL_FORMAT_REMOTE_ADDR: g_string_append_len(str, GSTR_LEN(vr->coninfo->remote_addr_str)); break; case AL_FORMAT_LOCAL_ADDR: g_string_append_len(str, GSTR_LEN(vr->coninfo->local_addr_str)); break; case AL_FORMAT_BYTES_RESPONSE: li_string_append_int(str, vr->vr_out->bytes_out); break; case AL_FORMAT_BYTES_RESPONSE_CLF: if (vr->vr_out->bytes_out) li_string_append_int(str, vr->vr_out->bytes_out); else g_string_append_c(str, '-'); break; case AL_FORMAT_DURATION_MICROSECONDS: li_string_append_int(str, (CUR_TS(vr->wrk) - vr->ts_started) * 1000 * 1000); break; case AL_FORMAT_ENV: tmp_gstr2 = li_environment_get(&vr->env, GSTR_LEN(e->key)); if (tmp_gstr2) al_append_escaped(str, tmp_gstr2); else g_string_append_c(str, '-'); break; case AL_FORMAT_FILENAME: if (phys->path->len) g_string_append_len(str, GSTR_LEN(phys->path)); else g_string_append_c(str, '-'); break; case AL_FORMAT_REQUEST_HEADER: li_http_header_get_all(vr->wrk->tmp_str, req->headers, GSTR_LEN(e->key)); if (vr->wrk->tmp_str->len) al_append_escaped(str, vr->wrk->tmp_str); else g_string_append_c(str, '-'); break; case AL_FORMAT_METHOD: g_string_append_len(str, GSTR_LEN(req->http_method_str)); break; case AL_FORMAT_RESPONSE_HEADER: li_http_header_get_all(vr->wrk->tmp_str, resp->headers, GSTR_LEN(e->key)); if (vr->wrk->tmp_str->len) al_append_escaped(str, vr->wrk->tmp_str); else g_string_append_c(str, '-'); break; case AL_FORMAT_LOCAL_PORT: switch (vr->coninfo->local_addr.addr->plain.sa_family) { case AF_INET: li_string_append_int(str, ntohs(vr->coninfo->local_addr.addr->ipv4.sin_port)); break; #ifdef HAVE_IPV6 case AF_INET6: li_string_append_int(str, ntohs(vr->coninfo->local_addr.addr->ipv6.sin6_port)); break; #endif default: g_string_append_c(str, '-'); break; } break; case AL_FORMAT_QUERY_STRING: if (req->uri.query->len) al_append_escaped(str, req->uri.query); else g_string_append_c(str, '-'); break; case AL_FORMAT_FIRST_LINE: g_string_append_len(str, GSTR_LEN(req->http_method_str)); g_string_append_c(str, ' '); al_append_escaped(str, req->uri.raw_orig_path); g_string_append_c(str, ' '); tmp_str = li_http_version_string(req->http_version, &len); g_string_append_len(str, tmp_str, len); break; case AL_FORMAT_STATUS_CODE: li_string_append_int(str, resp->http_status); break; case AL_FORMAT_TIME: /* todo: implement format string */ tmp_gstr2 = li_worker_current_timestamp(vr->wrk, LI_LOCALTIME, ald->ts_ndx); g_string_append_len(str, GSTR_LEN(tmp_gstr2)); break; case AL_FORMAT_DURATION_SECONDS: li_string_append_int(str, CUR_TS(vr->wrk) - vr->ts_started); break; case AL_FORMAT_AUTHED_USER: tmp_gstr2 = li_environment_get(&vr->env, CONST_STR_LEN("REMOTE_USER")); if (tmp_gstr2) g_string_append_len(str, GSTR_LEN(tmp_gstr2)); else g_string_append_c(str, '-'); break; case AL_FORMAT_PATH: g_string_append_len(str, GSTR_LEN(req->uri.path)); break; case AL_FORMAT_SERVER_NAME: if (CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_NAME).string) g_string_append_len(str, GSTR_LEN(CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_NAME).string)); else g_string_append_len(str, GSTR_LEN(req->uri.host)); break; case AL_FORMAT_HOSTNAME: if (req->uri.host->len) g_string_append_len(str, GSTR_LEN(req->uri.host)); else g_string_append_c(str, '-'); break; case AL_FORMAT_CONNECTION_STATUS: { /* was request completed? */ liConnection *con = li_connection_from_vrequest(vr); /* try to get a connection object */ if (con && (con->in->is_closed && con->raw_out->is_closed && 0 == con->raw_out->length)) { g_string_append_c(str, 'X'); } else { g_string_append_c(str, vr->coninfo->keep_alive ? '+' : '-'); } } break; case AL_FORMAT_BYTES_IN: li_string_append_int(str, vr->coninfo->stats.bytes_in); break; case AL_FORMAT_BYTES_OUT: li_string_append_int(str, vr->coninfo->stats.bytes_out); break; default: /* not implemented: { 'C', FALSE, AL_FORMAT_COOKIE } { 't', FALSE, AL_FORMAT_TIME }, (partially implemented) */ g_string_append_c(str, '?'); break; } } else { /* append normal string */ g_string_append_len(str, GSTR_LEN(e->key)); } } return str; }
static liHandlerResult dirlist(liVRequest *vr, gpointer param, gpointer *context) { GString *listing; liStatCacheEntry *sce; dirlist_data *dd; UNUSED(context); switch (vr->request.http_method) { case LI_HTTP_METHOD_GET: case LI_HTTP_METHOD_HEAD: break; default: return LI_HANDLER_GO_ON; } if (li_vrequest_is_handled(vr)) return LI_HANDLER_GO_ON; if (vr->physical.path->len == 0) return LI_HANDLER_GO_ON; switch (li_stat_cache_get_dirlist(vr, vr->physical.path, &sce)) { case LI_HANDLER_GO_ON: break; case LI_HANDLER_WAIT_FOR_EVENT: return LI_HANDLER_WAIT_FOR_EVENT; default: return LI_HANDLER_ERROR; } dd = param; if (sce->data.failed) { /* stat failed */ int e = sce->data.err; li_stat_cache_entry_release(vr, sce); switch (e) { case ENOENT: case ENOTDIR: return LI_HANDLER_GO_ON; case EACCES: if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR; vr->response.http_status = 403; return LI_HANDLER_GO_ON; default: VR_ERROR(vr, "stat('%s') failed: %s", sce->data.path->str, g_strerror(sce->data.err)); return LI_HANDLER_ERROR; } } else if (!S_ISDIR(sce->data.st.st_mode)) { li_stat_cache_entry_release(vr, sce); return LI_HANDLER_GO_ON; } else if (vr->request.uri.path->len == 0 || vr->request.uri.path->str[vr->request.uri.path->len-1] != '/') { li_stat_cache_entry_release(vr, sce); li_vrequest_redirect_directory(vr); return LI_HANDLER_GO_ON; } else { /* everything ok, we have the directory listing */ gboolean cachable; guint i, j; liStatCacheEntryData *sced; GString *mime_str, *encoded; GArray *directories, *files; gchar sizebuf[sizeof("999.9K")+1]; gchar datebuf[sizeof("2005-Jan-01 22:23:24")+1]; guint datebuflen; struct tm tm; gboolean hide; if (!li_vrequest_handle_direct(vr)) { li_stat_cache_entry_release(vr, sce); return LI_HANDLER_ERROR; } vr->response.http_status = 200; if (dd->debug) VR_DEBUG(vr, "dirlist for \"%s\", %u entries", sce->data.path->str, sce->dirlist->len); li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(dd->content_type)); li_etag_set_header(vr, &sce->data.st, &cachable); if (cachable) { vr->response.http_status = 304; li_stat_cache_entry_release(vr, sce); return LI_HANDLER_GO_ON; } /* temporary string for encoded names */ encoded = g_string_sized_new(64-1); /* seperate directories from other files */ directories = g_array_sized_new(FALSE, FALSE, sizeof(guint), 16); files = g_array_sized_new(FALSE, FALSE, sizeof(guint), sce->dirlist->len); for (i = 0; i < sce->dirlist->len; i++) { sced = &g_array_index(sce->dirlist, liStatCacheEntryData, i); hide = FALSE; /* ingore entries where the stat() failed */ if (sced->failed) continue; if (dd->hide_dotfiles && sced->path->str[0] == '.') continue; if (dd->hide_tildefiles && sced->path->str[sced->path->len-1] == '~') continue; for (j = 0; j < dd->exclude_suffix->len; j++) { if (li_string_suffix(sced->path, GSTR_LEN((GString*)g_ptr_array_index(dd->exclude_suffix, j)))) { hide = TRUE; break; } } if (hide) continue; for (j = 0; j < dd->exclude_prefix->len; j++) { if (li_string_prefix(sced->path, GSTR_LEN((GString*)g_ptr_array_index(dd->exclude_prefix, j)))) { hide = TRUE; break; } } if (hide) continue; if (S_ISDIR(sced->st.st_mode)) { if (dd->hide_directories) continue; g_array_append_val(directories, i); } else { if ((dd->include_header || dd->hide_header) && g_str_equal(sced->path, "HEADER.txt")) { if (dd->hide_header) continue; } else if ((dd->include_readme || dd->hide_readme) && g_str_equal(sced->path, "README.txt")) { if (dd->hide_readme) continue; } g_array_append_val(files, i); } } listing = g_string_sized_new(4*1024-1); g_string_append_printf(listing, html_header_start, vr->request.uri.path->str); if (dd->css) { /* custom css */ g_string_append_len(listing, CONST_STR_LEN(" <link rel=\"stylesheet\" type=\"text/css\" href=\"")); g_string_append_len(listing, GSTR_LEN(dd->css)); g_string_append_len(listing, CONST_STR_LEN("\" />\n")); } else { /* default css */ g_string_append_len(listing, CONST_STR_LEN(html_css)); } g_string_append_len(listing, CONST_STR_LEN(html_header_end)); try_append_file(vr, &listing, "HEADER.txt", dd->encode_header); g_string_append_printf(listing, html_table_start, vr->request.uri.path->str); if (0 != strcmp("/", vr->request.uri.path->str)) { g_string_append_printf(listing, html_table_row, '0', "../", "Parent Directory", (gint64)0, "", (gint64)0, "-", "Directory"); } /* list directories */ if (!dd->hide_directories) { for (i = 0; i < directories->len; i++) { sced = &g_array_index(sce->dirlist, liStatCacheEntryData, g_array_index(directories, guint, i)); localtime_r(&(sced->st.st_mtime), &tm); datebuflen = strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); datebuf[datebuflen] = '\0'; g_string_append_len(listing, CONST_STR_LEN(" <tr group=\"1\"><td><a href=\"")); li_string_encode(sced->path->str, encoded, LI_ENCODING_URI); g_string_append_len(listing, GSTR_LEN(encoded)); g_string_append_len(listing, CONST_STR_LEN("/\">")); li_string_encode(sced->path->str, encoded, LI_ENCODING_HTML); g_string_append_len(listing, GSTR_LEN(encoded)); g_string_append_len(listing, CONST_STR_LEN("</a></td><td class=\"modified\" val=\"")); li_string_append_int(listing, sced->st.st_mtime); g_string_append_len(listing, CONST_STR_LEN("\">")); g_string_append_len(listing, datebuf, datebuflen); g_string_append_len(listing, CONST_STR_LEN("</td>" "<td class=\"size\" val=\"0\">-</td>" "<td class=\"type\">Directory</td></tr>\n")); } } /*g_string_append_len(listing, CONST_STR_LEN("<tr><td colspan=\"4\"> </td></tr>\n"));*/ /* list files */ for (i = 0; i < files->len; i++) { sced = &g_array_index(sce->dirlist, liStatCacheEntryData, g_array_index(files, guint, i)); mime_str = li_mimetype_get(vr, sced->path); localtime_r(&(sced->st.st_mtime), &tm); datebuflen = strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); datebuf[datebuflen] = '\0'; dirlist_format_size(sizebuf, sced->st.st_size); g_string_append_len(listing, CONST_STR_LEN(" <tr group=\"2\"><td><a href=\"")); li_string_encode(sced->path->str, encoded, LI_ENCODING_URI); g_string_append_len(listing, GSTR_LEN(encoded)); g_string_append_len(listing, CONST_STR_LEN("\">")); li_string_encode(sced->path->str, encoded, LI_ENCODING_HTML); g_string_append_len(listing, GSTR_LEN(encoded)); g_string_append_len(listing, CONST_STR_LEN( "</a></td>" "<td class=\"modified\" val=\"")); li_string_append_int(listing, sced->st.st_mtime); g_string_append_len(listing, CONST_STR_LEN("\">")); g_string_append_len(listing, datebuf, datebuflen); g_string_append_len(listing, CONST_STR_LEN("</td><td class=\"size\" val=\"")); li_string_append_int(listing, sced->st.st_size); g_string_append_len(listing, CONST_STR_LEN("\">")); g_string_append(listing, sizebuf); g_string_append_len(listing, CONST_STR_LEN("</td><td class=\"type\">")); if (mime_str) { g_string_append_len(listing, GSTR_LEN(mime_str)); } else { g_string_append_len(listing, CONST_STR_LEN("application/octet-stream")); } g_string_append_len(listing, CONST_STR_LEN("</td></tr>\n")); /* g_string_append_printf(listing, html_table_row, sced->path->str, sced->path->str, (gint64)sced->st.st_mtime, datebuf, sced->st.st_size, sizebuf, mime_str ? mime_str->str : "application/octet-stream"); */ } g_string_append_len(listing, CONST_STR_LEN(html_table_end)); try_append_file(vr, &listing, "README.txt", dd->encode_readme); if (dd->include_sort) { g_string_append_len(listing, CONST_STR_LEN(javascript_sort)); } g_string_append_printf(listing, html_footer, CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_TAG).string->str); li_chunkqueue_append_string(vr->direct_out, listing); g_string_free(encoded, TRUE); g_array_free(directories, TRUE); g_array_free(files, TRUE); } li_stat_cache_entry_release(vr, sce); return LI_HANDLER_GO_ON; }
static liHandlerResult cache_etag_handle(liVRequest *vr, gpointer param, gpointer *context) { cache_etag_context *ctx = (cache_etag_context*) param; cache_etag_file *cfile = (cache_etag_file*) *context; GList *etag_entry; liHttpHeader *etag; struct stat st; GString *tmp_str = vr->wrk->tmp_str; liHandlerResult res; int err, fd; if (!cfile) { if (vr->request.http_method != LI_HTTP_METHOD_GET) return LI_HANDLER_GO_ON; LI_VREQUEST_WAIT_FOR_RESPONSE_HEADERS(vr); if (vr->response.http_status != 200) return LI_HANDLER_GO_ON; /* Don't cache static files if filter list is empty */ if (NULL == vr->filters_out_first && vr->backend_source->out->is_closed && 0 == vr->backend_source->out->mem_usage) return LI_HANDLER_GO_ON; etag_entry = li_http_header_find_first(vr->response.headers, CONST_STR_LEN("etag")); if (!etag_entry) return LI_HANDLER_GO_ON; /* no etag -> no caching */ if (li_http_header_find_next(etag_entry, CONST_STR_LEN("etag"))) { VR_ERROR(vr, "%s", "duplicate etag header in response, will not cache it"); return LI_HANDLER_GO_ON; } etag = (liHttpHeader*) etag_entry->data; cfile = cache_etag_file_create(createFileName(vr, ctx->path, etag)); *context = cfile; } res = li_stat_cache_get(vr, cfile->filename, &st, &err, &fd); if (res == LI_HANDLER_WAIT_FOR_EVENT) return res; if (res == LI_HANDLER_GO_ON) { liFilter *f; if (!S_ISREG(st.st_mode)) { VR_ERROR(vr, "Unexpected file type for cache file '%s' (mode %o)", cfile->filename->str, (unsigned int) st.st_mode); close(fd); return LI_HANDLER_GO_ON; /* no caching */ } cfile->hit_fd = fd; #ifdef FD_CLOEXEC fcntl(cfile->hit_fd, F_SETFD, FD_CLOEXEC); #endif if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "cache hit for '%s'", vr->request.uri.path->str); } cfile->hit_length = st.st_size; g_string_truncate(tmp_str, 0); li_string_append_int(tmp_str, st.st_size); li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(tmp_str)); f = li_vrequest_add_filter_out(vr, cache_etag_filter_hit, NULL, NULL, NULL); if (NULL != f) { li_chunkqueue_append_file_fd(f->out, NULL, 0, cfile->hit_length, cfile->hit_fd); f->out->is_closed = TRUE; cfile->hit_fd = -1; } cache_etag_file_free(cfile); *context = NULL; return LI_HANDLER_GO_ON; } if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "cache miss for '%s'", vr->request.uri.path->str); } if (!cache_etag_file_start(vr, cfile)) { cache_etag_file_free(cfile); *context = NULL; return LI_HANDLER_GO_ON; /* no caching */ } li_vrequest_add_filter_out(vr, cache_etag_filter_miss, cache_etag_filter_free, NULL, cfile); *context = NULL; return LI_HANDLER_GO_ON; }