mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
r13154@catbus: nickm | 2007-06-02 11:26:44 -0400
Server-side support for If-Modified-Since in HTTP requsts for v1 stuff, and for network-status documents. svn:r10451
This commit is contained in:
@@ -3,6 +3,12 @@ Changes in version 0.2.0.2-alpha - 2007-??-??
|
||||
- Fix an assertion failure related to servers without extra-info digests.
|
||||
Resolves bugs 441 and 442.
|
||||
|
||||
o Minor features (directory):
|
||||
- Support "If-Modified-Since" when answering HTTP requests for
|
||||
directories, running-routers documents, and network-status documents.
|
||||
(There's no need to support it for router descriptors, since those
|
||||
are downloaded by descriptor digest.)
|
||||
|
||||
o Minor build issues:
|
||||
- Clear up some MIPSPro compiler warnings.
|
||||
|
||||
|
||||
@@ -1127,6 +1127,64 @@ parse_iso_time(const char *cp, time_t *t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh),
|
||||
* parse it into <b>tm</b>. Return 0 on success, negative on failure. */
|
||||
int
|
||||
parse_http_time(const char *date, struct tm *tm)
|
||||
{
|
||||
const char *cp;
|
||||
char month[4];
|
||||
char wkday[4];
|
||||
int i;
|
||||
|
||||
tor_assert(tm);
|
||||
memset(tm, 0, sizeof(*tm));
|
||||
|
||||
/* First, try RFC1123 or RFC850 format: skip the weekday. */
|
||||
if ((cp = strchr(date, ','))) {
|
||||
++cp;
|
||||
if (sscanf(date, "%2d %3s %4d %2d:%2d:%2d GMT",
|
||||
&tm->tm_mday, month, &tm->tm_year,
|
||||
&tm->tm_hour, &tm->tm_min, &tm->tm_sec) == 6) {
|
||||
/* rfc1123-date */
|
||||
tm->tm_year -= 1900;
|
||||
} else if (sscanf(date, "%2d-%3s-%2d %2d:%2d:%2d GMT",
|
||||
&tm->tm_mday, month, &tm->tm_year,
|
||||
&tm->tm_hour, &tm->tm_min, &tm->tm_sec) == 6) {
|
||||
/* rfc850-date */
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* No comma; possibly asctime() format. */
|
||||
if (sscanf(date, "%3s %3s %2d %2d:%2d:%2d %4d",
|
||||
wkday, month, &tm->tm_mday,
|
||||
&tm->tm_hour, &tm->tm_min, &tm->tm_sec, &tm->tm_year) == 7) {
|
||||
tm->tm_year -= 1900;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
month[4] = '\0';
|
||||
/* Okay, now decode the month. */
|
||||
for (i = 0; i < 12; ++i) {
|
||||
if (!strcasecmp(MONTH_NAMES[i], month)) {
|
||||
tm->tm_mon = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (tm->tm_year < 0 ||
|
||||
tm->tm_mon < 1 || tm->tm_mon > 12 ||
|
||||
tm->tm_mday < 0 || tm->tm_mday > 31 ||
|
||||
tm->tm_hour < 0 || tm->tm_hour > 23 ||
|
||||
tm->tm_min < 0 || tm->tm_min > 59 ||
|
||||
tm->tm_sec < 0 || tm->tm_sec > 61)
|
||||
return -1; /* Out of range, or bad month. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* =====
|
||||
* File helpers
|
||||
* ===== */
|
||||
|
||||
@@ -198,6 +198,7 @@ int parse_rfc1123_time(const char *buf, time_t *t);
|
||||
void format_local_iso_time(char *buf, time_t t);
|
||||
void format_iso_time(char *buf, time_t t);
|
||||
int parse_iso_time(const char *buf, time_t *t);
|
||||
int parse_http_time(const char *buf, struct tm *tm);
|
||||
|
||||
/* File helpers */
|
||||
int write_all(int fd, const char *buf, size_t count, int isSocket);
|
||||
|
||||
+34
-4
@@ -1633,9 +1633,11 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
const char *body, size_t body_len)
|
||||
{
|
||||
size_t dlen;
|
||||
const char *cp;
|
||||
char *url = NULL;
|
||||
or_options_t *options = get_options();
|
||||
time_t if_modified_since = 0;
|
||||
char *header;
|
||||
|
||||
/* We ignore the body of a GET request. */
|
||||
(void)body;
|
||||
(void)body_len;
|
||||
@@ -1648,6 +1650,15 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
write_http_status_line(conn, 400, "Bad request");
|
||||
return 0;
|
||||
}
|
||||
if ((header = http_get_header(headers, "If-Modified-Since: "))) {
|
||||
struct tm tm;
|
||||
if (parse_http_time(header, &tm) == 0) {
|
||||
if_modified_since = tor_timegm(&tm);
|
||||
}
|
||||
/* The correct behavior on a malformed If-Modified-Since header is to
|
||||
* act as if no If-Modified-Since header had been given. */
|
||||
tor_free(header);
|
||||
}
|
||||
log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
|
||||
|
||||
if (!strcmp(url,"/tor/") || !strcmp(url,"/tor/dir.z")) { /* dir fetch */
|
||||
@@ -1664,6 +1675,12 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
tor_free(url);
|
||||
return 0;
|
||||
}
|
||||
if (d->published < if_modified_since) {
|
||||
write_http_status_line(conn, 304, "Not modified");
|
||||
tor_free(url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlen = deflated ? d->dir_z_len : d->dir_len;
|
||||
|
||||
if (global_write_bucket_low(TO_CONN(conn), dlen, 1)) {
|
||||
@@ -1699,8 +1716,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
if (!strcmp(url,"/tor/running-routers") ||
|
||||
!strcmp(url,"/tor/running-routers.z")) { /* running-routers fetch */
|
||||
int deflated = !strcmp(url,"/tor/running-routers.z");
|
||||
dlen = dirserv_get_runningrouters(&cp, deflated);
|
||||
if (!dlen) { /* we failed to create/cache cp */
|
||||
cached_dir_t *d = dirserv_get_runningrouters();
|
||||
if (!d) {
|
||||
write_http_status_line(conn, 503, "Directory unavailable");
|
||||
/* try to get a new one now */
|
||||
if (!already_fetching_directory(DIR_PURPOSE_FETCH_RUNNING_LIST))
|
||||
@@ -1708,6 +1725,13 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
tor_free(url);
|
||||
return 0;
|
||||
}
|
||||
if (d->published < if_modified_since) {
|
||||
write_http_status_line(conn, 304, "Not modified");
|
||||
tor_free(url);
|
||||
return 0;
|
||||
}
|
||||
dlen = deflated ? d->dir_z_len : d->dir_len;
|
||||
|
||||
if (global_write_bucket_low(TO_CONN(conn), dlen, 1)) {
|
||||
log_info(LD_DIRSERV,
|
||||
"Client asked for running-routers, but we've been "
|
||||
@@ -1722,7 +1746,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
deflated?"application/octet-stream":"text/plain",
|
||||
deflated?"deflate":"identity",
|
||||
RUNNINGROUTERS_CACHE_LIFETIME);
|
||||
connection_write_to_buf(cp, strlen(cp), TO_CONN(conn));
|
||||
connection_write_to_buf(deflated ? d->dir_z : d->dir, dlen, TO_CONN(conn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1751,6 +1775,12 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
smartlist_free(dir_fps);
|
||||
return 0;
|
||||
}
|
||||
if (dirserv_statuses_are_old(dir_fps, if_modified_since)) {
|
||||
write_http_status_line(conn, 304, "Not modified");
|
||||
smartlist_free(dir_fps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlen = dirserv_estimate_data_size(dir_fps, 0, deflated);
|
||||
if (global_write_bucket_low(TO_CONN(conn), dlen, 2)) {
|
||||
log_info(LD_DIRSERV,
|
||||
|
||||
+24
-3
@@ -1279,6 +1279,7 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/** Helper: If we're authoritative and <b>auth_src</b> is set, use
|
||||
* <b>auth_src</b>, otherwise use <b>cache_src</b>. If we're using
|
||||
* <b>auth_src</b> and it's been <b>dirty</b> for at least
|
||||
@@ -1313,6 +1314,7 @@ dirserv_get_obj(const char **out,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Return the most recently generated encoded signed v1 directory,
|
||||
* generating a new one as necessary. If not a v1 authoritative directory
|
||||
@@ -1418,10 +1420,10 @@ generate_runningrouters(void)
|
||||
/** Set *<b>rr</b> to the most recently generated encoded signed
|
||||
* running-routers list, generating a new one as necessary. Return the
|
||||
* size of the directory on success, and 0 on failure. */
|
||||
size_t
|
||||
dirserv_get_runningrouters(const char **rr, int compress)
|
||||
cached_dir_t *
|
||||
dirserv_get_runningrouters(void)
|
||||
{
|
||||
return dirserv_get_obj(rr, compress,
|
||||
return dirserv_pick_cached_dir_obj(
|
||||
&cached_runningrouters, &the_runningrouters,
|
||||
runningrouters_is_dirty,
|
||||
generate_runningrouters,
|
||||
@@ -2247,6 +2249,25 @@ dirserv_test_reachability(int try_all)
|
||||
ctr = (ctr + 1) % 128;
|
||||
}
|
||||
|
||||
/** Return true iff every networkstatus listed in <b>fps</b> is older
|
||||
* than <b>cutoff</b>. */
|
||||
int
|
||||
dirserv_statuses_are_old(smartlist_t *fps, time_t cutoff)
|
||||
{
|
||||
SMARTLIST_FOREACH(fps, const char *, digest,
|
||||
{
|
||||
cached_dir_t *d;
|
||||
if (router_digest_is_me(digest) && the_v2_networkstatus)
|
||||
d = the_v2_networkstatus;
|
||||
else
|
||||
d = digestmap_get(cached_v2_networkstatus, digest);
|
||||
if (d && d->published > cutoff)
|
||||
return 0;
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Return an approximate estimate of the number of bytes that will
|
||||
* be needed to transmit the server descriptors (if is_serverdescs --
|
||||
* they can be either d/ or fp/ queries) or networkstatus objects (if
|
||||
|
||||
+2
-1
@@ -2670,7 +2670,7 @@ int dirserv_dump_directory_to_string(char **dir_out,
|
||||
int complete);
|
||||
void directory_set_dirty(void);
|
||||
cached_dir_t *dirserv_get_directory(void);
|
||||
size_t dirserv_get_runningrouters(const char **rr, int compress);
|
||||
cached_dir_t *dirserv_get_runningrouters(void);
|
||||
void dirserv_set_cached_directory(const char *directory, time_t when,
|
||||
int is_running_routers);
|
||||
void dirserv_set_cached_networkstatus_v2(const char *directory,
|
||||
@@ -2693,6 +2693,7 @@ void dirserv_test_reachability(int try_all);
|
||||
int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
|
||||
int complain);
|
||||
int dirserv_would_reject_router(routerstatus_t *rs);
|
||||
int dirserv_statuses_are_old(smartlist_t *fps, time_t cutoff);
|
||||
size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
|
||||
int compressed);
|
||||
int routerstatus_format_entry(char *buf, size_t buf_len,
|
||||
|
||||
Reference in New Issue
Block a user