| 
					
				 | 
			
			
				@@ -61,6 +61,13 @@ static void note_request(const char *key, size_t bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #define X_ADDRESS_HEADER "X-Your-Address-Is: " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* HTTP cache control: how long do we tell proxies they can cache things? */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define FULL_DIR_CACHE_LIFETIME (60*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define RUNNINGROUTERS_CACHE_LIFETIME (20*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define NETWORKSTATUS_CACHE_LIFETIME (5*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define ROUTERDESC_CACHE_LIFETIME (30*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define ROBOTS_CACHE_LIFETIME (24*60*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /********* END VARIABLES ************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Return true iff the directory purpose 'purpose' must use an 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1297,19 +1304,27 @@ write_http_status_line(dir_connection_t *conn, int status, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   connection_write_to_buf(buf, strlen(buf), TO_CONN(conn)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-/** DOCDOC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Write the header for an HTTP/1.0 response onto <b>conn</b>->outbuf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * with <b>type</b> as the Content-Type. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * If <b>length</b> is nonnegative, it is the Content-Length. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * If <b>encoding</b> is provided, it is the Content-Encoding. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * If <b>cache_lifetime</b> is greater than 0, the content may be cached for 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * up to cache_lifetime seconds.  Otherwise, the content may not be cached. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 write_http_response_header(dir_connection_t *conn, ssize_t length, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                           const char *type, const char *encoding) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           const char *type, const char *encoding, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           int cache_lifetime) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char date[RFC1123_TIME_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char tmp[1024]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   char *cp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  time_t now = time(NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_assert(conn); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_assert(type); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  format_rfc1123_time(date, time(NULL)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  format_rfc1123_time(date, now); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   cp = tmp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_snprintf(cp, sizeof(tmp), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Type: %s\r\n" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1326,6 +1341,20 @@ write_http_response_header(dir_connection_t *conn, ssize_t length, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                  "Content-Length: %ld\r\n", (long)length); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     cp += strlen(cp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (cache_lifetime > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char expbuf[RFC1123_TIME_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    format_rfc1123_time(expbuf, now + cache_lifetime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* We could say 'Cache-control: max-age=%d' here if we start doing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * http/1.1 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    tor_snprintf(cp, sizeof(tmp)-(cp-tmp), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 "Expires: %s\r\n", expbuf); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cp += strlen(cp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* We could say 'Cache-control: no-cache' here if we start doing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * http/1.1 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cp += strlen(cp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (sizeof(tmp)-(cp-tmp) > 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     memcpy(cp, "\r\n", 3); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   else 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1484,7 +1513,8 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				               deflated?"deflated ":""); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     write_http_response_header(conn, dlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                           deflated?"application/octet-stream":"text/plain", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                          deflated?"deflate":"identity"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          deflated?"deflate":"identity", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          FULL_DIR_CACHE_LIFETIME); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     conn->cached_dir = d; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     conn->cached_dir_offset = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (! deflated) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1513,7 +1543,8 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     write_http_response_header(conn, dlen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                  deflated?"application/octet-stream":"text/plain", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 deflated?"deflate":"identity"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 deflated?"deflate":"identity", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 RUNNINGROUTERS_CACHE_LIFETIME); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     connection_write_to_buf(cp, strlen(cp), TO_CONN(conn)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1545,9 +1576,9 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // note_request(request_type,dlen); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     write_http_response_header(conn, -1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                   deflated?"application/octet_stream":"text/plain", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                   deflated?"deflate":NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 deflated?"application/octet_stream":"text/plain", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 deflated?"deflate":NULL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 smartlist_len(dir_fps) == 1 ? NETWORKSTATUS_CACHE_LIFETIME:0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     conn->fingerprint_stack = dir_fps; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (! deflated) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1565,23 +1596,31 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     int res; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const char *msg; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const char *request_type = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int cache_lifetime = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (deflated) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       url[url_len-2] = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     conn->fingerprint_stack = smartlist_create(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     res = dirserv_get_routerdesc_fingerprints(conn->fingerprint_stack, url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                               &msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (!strcmpstart(url, "/tor/server/fp/")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!strcmpstart(url, "/tor/server/fp/")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       request_type = deflated?"/tor/server/fp.z":"/tor/server/fp"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (!strcmpstart(url, "/tor/server/authority")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (smartlist_len(conn->fingerprint_stack) == 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cache_lifetime = ROUTERDESC_CACHE_LIFETIME; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if (!strcmpstart(url, "/tor/server/authority")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       request_type = deflated?"/tor/server/authority.z": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         "/tor/server/authority"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (!strcmpstart(url, "/tor/server/all")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      cache_lifetime = ROUTERDESC_CACHE_LIFETIME; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if (!strcmpstart(url, "/tor/server/all")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       request_type = deflated?"/tor/server/all.z":"/tor/server/all"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (!strcmpstart(url, "/tor/server/d/")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      cache_lifetime = FULL_DIR_CACHE_LIFETIME; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if (!strcmpstart(url, "/tor/server/d/")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       request_type = deflated?"/tor/server/d.z":"/tor/server/d"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (smartlist_len(conn->fingerprint_stack) == 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cache_lifetime = ROUTERDESC_CACHE_LIFETIME; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       request_type = "/tor/server/?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (!strcmpstart(url, "/tor/server/d/")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       conn->dir_spool_src = DIR_SPOOL_SERVER_BY_DIGEST; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     else 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1592,7 +1631,7 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       write_http_response_header(conn, -1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                      deflated?"application/octet_stream":"text/plain", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     deflated?"deflate":NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     deflated?"deflate":NULL, cache_lifetime); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       if (deflated) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       /* Prime the connection with some data. */ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1613,7 +1652,7 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     switch (rend_cache_lookup_desc(query, versioned?-1:0, &descp, &desc_len)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       case 1: /* valid */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         write_http_response_header(conn, desc_len, "application/octet-stream", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                   NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   NULL, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         note_request("/tor/rendezvous?/", desc_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         /* need to send descp separately, because it may include nuls */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         connection_write_to_buf(descp, desc_len, TO_CONN(conn)); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1632,7 +1671,7 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (!strcmpstart(url,"/tor/bytes.txt")) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     char *bytes = directory_dump_request_log(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     size_t len = strlen(bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    write_http_response_header(conn, len, "text/plain", NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_http_response_header(conn, len, "text/plain", NULL, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     connection_write_to_buf(bytes, len, TO_CONN(conn)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(url); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1643,7 +1682,8 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                            rewritten to /tor/robots.txt */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     char robots[] = "User-agent: *\r\nDisallow: /\r\n"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     size_t len = strlen(robots); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    write_http_response_header(conn, len, "text/plain", NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_http_response_header(conn, len, "text/plain", NULL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                               ROBOTS_CACHE_LIFETIME); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     connection_write_to_buf(robots, len, TO_CONN(conn)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return 0; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1665,7 +1705,7 @@ directory_handle_command_get(dir_connection_t *conn, char *headers, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     dlen = strlen(new_directory); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    write_http_response_header(conn, dlen, "text/plain", "identity"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_http_response_header(conn, dlen, "text/plain", "identity", 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     connection_write_to_buf(new_directory, dlen, TO_CONN(conn)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tor_free(new_directory); 
			 |