[tor-commits] [stegotorus/master] Candidate fixes for bugs exposed by running the automated tests on HTTP steg. Not 100% working yet.

zwol at torproject.org zwol at torproject.org
Fri Jul 20 23:17:07 UTC 2012


commit eb200a8333b4cbb4b239b2fcfbaeeb3874c298ac
Author: Zack Weinberg <zackw at panix.com>
Date:   Sun Apr 1 11:34:27 2012 -0700

    Candidate fixes for bugs exposed by running the automated tests on HTTP steg.  Not 100% working yet.
---
 src/network.cc       |    8 ++-
 src/protocol/chop.cc |  120 ++++++++++++++++++++++++++-----------------------
 src/steg/jsSteg.cc   |    4 +-
 src/steg/payloads.cc |   12 ++---
 src/test/test_tl.py  |    9 +++-
 5 files changed, 81 insertions(+), 72 deletions(-)

diff --git a/src/network.cc b/src/network.cc
index 7eac64d..55944fe 100644
--- a/src/network.cc
+++ b/src/network.cc
@@ -444,12 +444,14 @@ downstream_flush_cb(struct bufferevent *bev, void *arg)
 {
   conn_t *conn = (conn_t *)arg;
   size_t remain = evbuffer_get_length(bufferevent_get_output(bev));
-  log_debug(conn, "%lu bytes still to transmit%s%s",
+  log_debug(conn, "%lu bytes still to transmit%s%s%s",
             (unsigned long)remain,
             conn->connected ? "" : " (not connected)",
-            conn->flushing ? "" : " (not flushing)");
+            conn->flushing ? "" : " (not flushing)",
+            conn->circuit() ? "" : " (no circuit)");
 
-  if (remain == 0 && conn->flushing && conn->connected) {
+  if (remain == 0 && ((conn->flushing && conn->connected)
+                      || !conn->circuit())) {
     bufferevent_disable(bev, EV_WRITE);
     if (bufferevent_get_enabled(bev)) {
       log_debug(conn, "sending EOF downstream");
diff --git a/src/protocol/chop.cc b/src/protocol/chop.cc
index 27e03ab..a46d0ac 100644
--- a/src/protocol/chop.cc
+++ b/src/protocol/chop.cc
@@ -667,46 +667,49 @@ chop_circuit_t::send()
 {
   circuit_disarm_flush_timer(this);
 
-  if (downstreams.empty()) {
-    // We have no connections, but we must send.  If we're the client,
-    // reopen our outbound connections; the on-connection event will
-    // bring us back here.  If we're the server, we have to just
-    // twiddle our thumbs and hope the client reconnects.
-    log_debug(this, "no downstream connections");
-    if (config->mode != LSN_SIMPLE_SERVER)
-      circuit_reopen_downstreams(this);
-    else
-      circuit_arm_axe_timer(this, axe_interval());
-    return 0;
-  }
-
   struct evbuffer *xmit_pending = bufferevent_get_input(up_buffer);
   size_t avail = evbuffer_get_length(xmit_pending);
   size_t avail0 = avail;
 
-  // Send at least one block, even if there is no real data to send.
-  do {
-    log_debug(this, "%lu bytes to send", (unsigned long)avail);
-    size_t blocksize;
-    chop_conn_t *target = pick_connection(avail, &blocksize);
-    if (!target) {
-      // this is not an error; it can happen e.g. when the server has
-      // something to send immediately and the client hasn't spoken yet
-      log_debug(this, "no target connection available");
-      break;
-    }
+  if (downstreams.empty()) {
+    log_debug(this, "no downstream connections");
+  } else {
+    // Send at least one block, even if there is no real data to send.
+    do {
+      log_debug(this, "%lu bytes to send", (unsigned long)avail);
+      size_t blocksize;
+      chop_conn_t *target = pick_connection(avail, &blocksize);
+      if (!target) {
+        // this is not an error; it can happen e.g. when the server has
+        // something to send immediately and the client hasn't spoken yet
+        log_debug(this, "no target connection available");
+        break;
+      }
 
-    if (send_targeted(target, blocksize))
-      return -1;
+      if (send_targeted(target, blocksize))
+        return -1;
 
-    avail = evbuffer_get_length(xmit_pending);
-  } while (avail > 0);
+      avail = evbuffer_get_length(xmit_pending);
+    } while (avail > 0);
+  }
 
-  if (avail0 > avail) // we transmitted some real data
-    dead_cycles = 0;
-  else {
+  if (avail0 == avail) { // no forward progress
     dead_cycles++;
     log_debug(this, "%u dead cycles", dead_cycles);
+
+    // If there was real data and we didn't make any progress on it,
+    // or if there are no downstream connections at all, and we're the
+    // client, try opening new connections.  If we're the server, we
+    // have to just twiddle our thumbs and hope the client does that.
+    // Note that due to the sliding window of receive blocks, there is
+    // a hard upper limit of 127 outstanding connections (that is,
+    // half the receive window).
+    if ((avail0 > 0 && downstreams.size() < 127) || downstreams.empty()) {
+      if (config->mode != LSN_SIMPLE_SERVER)
+        circuit_reopen_downstreams(this);
+      else
+        circuit_arm_axe_timer(this, axe_interval());
+    }
   }
 
   return check_for_eof();
@@ -843,6 +846,10 @@ chop_circuit_t::send_targeted(chop_conn_t *conn, size_t d, size_t p, opcode_t f,
   send_seq++;
   if (f == op_FIN)
     sent_fin = true;
+  if ((f == op_DAT || f == op_FIN) && d > 0)
+    // We are making forward progress if we are _either_ sending or
+    // receiving data.
+    dead_cycles = 0;
   return 0;
 }
 
@@ -868,34 +875,32 @@ chop_circuit_t::pick_connection(size_t desired, size_t *blocksize)
   for (unordered_set<chop_conn_t *>::iterator i = downstreams.begin();
        i != downstreams.end(); i++) {
     chop_conn_t *conn = *i;
-    // We can only use candidates that have a steg target already.
-    if (conn->steg) {
-      // Find the connections whose transmit rooms are closest to the
-      // desired transmission length from both directions.
-      size_t room = conn->steg->transmit_room();
+    if (!conn->steg) {
+      log_debug(conn, "offers 0 bytes (no steg)");
+      continue;
+    }
 
-      if (room <= MIN_BLOCK_SIZE)
-	room = 0;
+    size_t room = conn->steg->transmit_room();
 
-      if (room > MAX_BLOCK_SIZE)
-        room = MAX_BLOCK_SIZE;
+    if (room <= MIN_BLOCK_SIZE)
+      room = 0;
 
-      log_debug(conn, "offers %lu bytes (%s)", (unsigned long)room,
-                conn->steg->cfg()->name());
+    if (room > MAX_BLOCK_SIZE)
+      room = MAX_BLOCK_SIZE;
 
-      if (room >= desired) {
-        if (room < minabove) {
-          minabove = room;
-          targabove = conn;
-        }
-      } else {
-        if (room > maxbelow) {
-          maxbelow = room;
-          targbelow = conn;
-        }
+    log_debug(conn, "offers %lu bytes (%s)", (unsigned long)room,
+              conn->steg->cfg()->name());
+
+    if (room >= desired) {
+      if (room < minabove) {
+        minabove = room;
+        targabove = conn;
       }
     } else {
-      log_debug(conn, "offers 0 bytes (no steg)");
+      if (room > maxbelow) {
+        maxbelow = room;
+        targbelow = conn;
+      }
     }
   }
 
@@ -943,6 +948,9 @@ chop_circuit_t::process_queue()
           log_info(this, "protocol error: data after FIN");
           pending_error = true;
         } else {
+          // We are making forward progress if we are _either_ sending or
+          // receiving data.
+          dead_cycles = 0;
           if (evbuffer_add_buffer(bufferevent_get_output(up_buffer),
                                   blk.data)) {
             log_warn(this, "buffer transfer failure");
@@ -989,8 +997,6 @@ chop_circuit_t::process_queue()
   }
 
   log_debug(this, "processed %u blocks", count);
-  if (count > 0)
-    dead_cycles = 0;
   if (sent_error)
     return -1;
 
@@ -1318,8 +1324,10 @@ chop_conn_t::send()
   // comes in for a stale circuit.
   if (upstream) {
     log_debug(this, "must send");
-    if (upstream->send_targeted(this))
+    if (upstream->send_targeted(this)) {
+      upstream->drop_downstream(this);
       conn_do_flush(this);
+    }
 
   } else {
     log_debug(this, "must send (no upstream)");
diff --git a/src/steg/jsSteg.cc b/src/steg/jsSteg.cc
index 60893ef..06d31e4 100644
--- a/src/steg/jsSteg.cc
+++ b/src/steg/jsSteg.cc
@@ -778,11 +778,9 @@ http_server_JS_transmit (payloads& pl, struct evbuffer *source, conn_t *conn,
 
   free(iv);
 
-  log_debug("SERVER encoded data in hex string (len %d):", datalen);
+  //log_debug("SERVER encoded data in hex string (len %d):", datalen);
   //    buf_dump((unsigned char*)data, datalen, stderr);
 
-
-
   if (get_payload(pl, content_type, datalen, &jsTemplate, &jsLen) == 1) {
     log_debug("SERVER found the applicable HTTP response template with size %d", jsLen);
   } else {
diff --git a/src/steg/payloads.cc b/src/steg/payloads.cc
index d9546d2..8a766c5 100644
--- a/src/steg/payloads.cc
+++ b/src/steg/payloads.cc
@@ -1158,10 +1158,7 @@ int init_JS_payload_pool(payloads& pl, int len, int type, int minCapacity) {
     }
   }
 
-  
   pl.max_JS_capacity = maxPayloadCap;
-
-
   pl.initTypePayload[contentType] = 1;
   pl.typePayloadCount[contentType] = cnt;
   log_debug("init_payload_pool: typePayloadCount for contentType %d = %d",
@@ -1243,10 +1240,8 @@ int  init_HTML_payload_pool(payloads& pl, int len, int type, int minCapacity) {
     }
   }
 
-  
   pl.max_HTML_capacity = maxPayloadCap;
-
-
+  pl.initTypePayload[contentType] = 1;
   pl.typePayloadCount[contentType] = cnt;
   log_debug("init_payload_pool: typePayloadCount for contentType %d = %d",
      contentType, pl.typePayloadCount[contentType]); 
@@ -1436,8 +1431,9 @@ int get_next_payload (payloads& pl, int contentType, char** buf,
 int get_payload (payloads& pl, int contentType, int cap, char** buf, int* size) {
   int r, i, cnt, found = 0, numCandidate = 0, first, best, current;
 
-  log_debug("get_payload: contentType = %d, initTypePayload = %d, typePayloadCount = %d",
-      contentType, pl.initTypePayload[contentType], pl.typePayloadCount[contentType]);
+  log_debug("contentType = %d, initTypePayload = %d, typePayloadCount = %d",
+            contentType, pl.initTypePayload[contentType],
+            pl.typePayloadCount[contentType]);
 
   if (contentType <= 0 ||
       contentType >= MAX_CONTENT_TYPE ||
diff --git a/src/test/test_tl.py b/src/test/test_tl.py
index 9987c7d..eb61f31 100644
--- a/src/test/test_tl.py
+++ b/src/test/test_tl.py
@@ -87,8 +87,13 @@ class TimelineTest(object):
     #        "127.0.0.1:5010","embed",
     #        ))
 
-    # NOTE: 'http' steg presently cannot be tested using this system
-    # because the trace pools are process-global rather than per-listener.
+    def test_http(self):
+        self.doTest("chop",
+           ("chop", "server", "127.0.0.1:5001",
+            "127.0.0.1:5010","http","127.0.0.1:5011","http",
+            "chop", "client", "127.0.0.1:4999",
+            "127.0.0.1:5010","http","127.0.0.1:5011","http",
+            ))
 
 # Synthesize TimelineTest+TestCase subclasses for every 'tl_*' file in
 # the test directory.





More information about the tor-commits mailing list