diff options
author | Nick Mathewson <nickm@torproject.org> | 2007-06-02 15:26:57 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2007-06-02 15:26:57 +0000 |
commit | aee7f016242c0fea9243be1c82a67b804bfb6792 (patch) | |
tree | 6e28814091a4325047b48c2d95131f21ef25a846 | |
parent | 80954dcd2faefe477bd0a3b646ffd3dce3c56481 (diff) | |
download | tor-aee7f016242c0fea9243be1c82a67b804bfb6792.tar.gz tor-aee7f016242c0fea9243be1c82a67b804bfb6792.zip |
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
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | src/common/util.c | 58 | ||||
-rw-r--r-- | src/common/util.h | 1 | ||||
-rw-r--r-- | src/or/directory.c | 38 | ||||
-rw-r--r-- | src/or/dirserv.c | 27 | ||||
-rw-r--r-- | src/or/or.h | 3 |
6 files changed, 125 insertions, 8 deletions
@@ -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. diff --git a/src/common/util.c b/src/common/util.c index 14013edb10..74279cee7a 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -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 * ===== */ diff --git a/src/common/util.h b/src/common/util.h index ab8aea86e8..0f0902452f 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -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); diff --git a/src/or/directory.c b/src/or/directory.c index 087029f4c8..3ef2a2233e 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -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, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 31d85a2495..82a42af118 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -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 diff --git a/src/or/or.h b/src/or/or.h index 26ec35b4c5..9680b87685 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -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, |