webcache.tex
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:48k
源码类别:

通讯编程

开发平台:

Visual C++

  1. chapter{Web cache as an application}
  2. label{chap:webcache}
  3. All applications described above are ``virtual'' applications, in the sense
  4. that they do not actually transfer their own data in the simulator; all 
  5. that matter is the emph{size} and the emph{time} when data are transferred.
  6. Sometimes we may want applications to transfer their own data in simulations.
  7. One such example is web caching, where we want HTTP servers to send HTTP 
  8. headers to caches and clients. These headers contain 
  9. page modification time information and other caching directives, which are 
  10. important for some cache consistency algorithms.
  11. In the following, we first describe general issues regarding
  12. transmitting application-level data in ns, then we discuss special
  13. issues, as well as APIs, related to transmitting application data
  14. using TCP as transport. We will then proceed to discuss the internal
  15. design of HTTP client, server, and proxy cache. 
  16. section{Using application-level data in ns}
  17. begin{figure}[tb]
  18.   begin{center}
  19.     centerline{includegraphics{app-dataflow}}
  20.     caption{Examples of application-level data flow}
  21.     label{fig:app-dataflow}
  22.   end{center}
  23. end{figure}
  24. In order to transmit application-level data in ns, we provide a 
  25. uniform structure to pass data among applications, and to
  26. pass data from applications to transport agents (Figure
  27. ref{fig:app-dataflow}). It has three major components: 
  28. a representation of a uniform application-level data unit (ADU), a 
  29. common interface to pass data between applications, and two mechanisms
  30. to pass data between applications and transport agents.
  31. subsection{ADU} 
  32. The functionality of an ADU is similar to that of a Packet. It needs to
  33. pack user data into an array, which is then included in the user data
  34. area of an ns packet by an Agent (this is not supported by current
  35. Agents. User must derive new agents to accept user data from
  36. applications, or use an wrapper like TcpApp. We'll discuss this
  37. later). 
  38. Compared with Packet, ADU provides this functionality in a different
  39. way. In Packet, a common area is allocated for all packet headers; an
  40. offset is used to access different headers in this area. In ADU this
  41. is not applicable, because some ADU allocates their space dynamically
  42. according the the availability of user data. For example, if we want
  43. to deliver an OTcl script between applications, the size of the script
  44. is undetermined beforehand. Therefore, we choose a less efficient but
  45. more flexible method. Each ADU defines its own data members, and
  46. provides methods to serialize them (i.e., pack data into an array and
  47. extract them from an array). For example, in the abstract base class
  48. of all ADU, AppData, we have:
  49. begin{program}
  50.         class AppData {
  51.         private:
  52.                 AppDataType type_;  // ADU type
  53.         public:
  54.                 struct hdr {
  55.                         AppDataType type_;
  56.                 };
  57.         public:
  58.                 AppData(char* b) {
  59.                         assert(b != NULL);
  60.                         type_ = ((hdr *)b)->type_;
  61.                 }
  62.                 virtual void pack(char* buf) const;
  63.         }
  64. end{program}
  65. Here code{pack(char* buf)} is used to write an AppData object
  66. into an array, and code{AppData(char* b)} is used to build a new
  67. AppData from a ``serialized'' copy of the object in an array.
  68. When deriving new ADU from the base class, users may add more data,
  69. but at the same time a new code{pack(char *b)} and a new constructor 
  70. should be provided to write and read those new data members from an
  71. array. For an example as how to derive from an ADU, look at 
  72. ns/webcache/http-aux.h.
  73. subsection{Passing data between applications}
  74. The base class of Application, Process, allows applications to pass
  75. data or request data between each other. It is defined as follows:
  76. begin{program}
  77.         class Process {
  78.         public: 
  79.                 Process() : target_(0) {}
  80.                 inline Process*& target() { return target_; }
  81.                 virtual void process_data(int size, char* data) = 0;
  82.                 virtual void send_data(int size, char* data = 0);
  83.         protected:
  84.                 Process* target_;
  85.         };
  86. end{program}
  87. Process enables Application to link together. 
  88. %{bf TBA}
  89. subsection{Transmitting user data over UDP}
  90. Currently there is no support in class Agent to transmit user
  91. data. There are two ways to transmit a serialized ADU through transport
  92. agents. First, for UDP agent (and all agents derived from there), we
  93. can derive from class UDP and add a new method
  94. code{send(int nbytes, char *userdata)} to pass user data from
  95. Application to Agent. To pass data from an Agent to an Application is
  96. somewhat trickier: each agent has a pointer to its attached
  97. application, we dynamically cast this pointer to an AppConnector and
  98. then call code{AppConnector::process_data()}.
  99. As an example, we illustrate how class HttpInvalAgent is
  100. implemented. It is based on UDP, and is intended to deliver web cache
  101. invalidation messages (ns/webcache/inval-agent.h). It is defined as:
  102. begin{program}
  103.         class HttpInvalAgent : public Agent {
  104.         public: 
  105.                 HttpInvalAgent();
  106.                 virtual void recv(Packet *, Handler *);
  107.                 virtual void send(int realsize, AppData* data);
  108.         protected:
  109.                 int off_inv_;
  110.         };
  111. end{program}
  112. Here code{recv(Packet*, Handler*)} overridden to extract user data,
  113. and a new code{send(int, AppData*)} is provided to include user data
  114. in packetes. An application (HttpApp) is attached to an HttpInvalAgent
  115. using code{Agent::attachApp()} (a dynamic cast is needed). In
  116. code{send()}, the following code is used to write user data from
  117. AppData to the user data area in a packet:
  118. begin{program}
  119.         Packet *pkt = allocpkt(data->size());
  120.         hdr_inval *ih = (hdr_inval *)pkt->access(off_inv_);
  121.         ih->size() = data->size();
  122.         char *p = (char *)pkt->accessdata();
  123.         data->pack(p);
  124. end{program}
  125. In code{recv()}, the following code is used to read user data from
  126. packet and to deliver to the attached application:
  127. begin{program}
  128.         hdr_inval *ih = (hdr_inval *)pkt->access(off_inv_);
  129.         ((HttpApp*)app_)->process_data(ih->size(), (char *)pkt->accessdata());
  130.         Packet::free(pkt);
  131. end{program}
  132. subsection{Transmitting user data over TCP}
  133. label{sec:webcache-tcpapp}
  134. Transmitting user data using TCP is trickier than doing that over UDP,
  135. mainly because of TCP's reassembly queue is only available for
  136. FullTcp. We deal with this problem by abstracting a TCP connection as
  137. a FIFO pipe. 
  138. As indicated in section ref{sec:upcalls}, transmission of application data
  139. can be implemented via agent upcalls. Assuming we are using TCP agents, 
  140. all data are delivered in sequence, which means we can view the TCP 
  141. connection as a FIFO pipe. We emulate user data transmission over TCP
  142. as follows. We first provide buffer 
  143. for application data at the sender. Then we count the bytes received at the 
  144. receiver. When the receiver has got all bytes of the current data transmission,
  145. it then gets the data directly from the sender. Class Application/TcpApp is 
  146. used to implement this functionality.
  147. A TcpApp object contains a pointer to a transport agent, presumably either
  148. a FullTcp or a SimpleTcp.
  149. footnote{A SimpleTcp agent is used solely for web caching simulations. It 
  150. is actually an UDP agent. It has neither error recovery nor flow/congestion
  151. control. It doesn't do packet segmentation. Assuming a loss-free network 
  152. and in-order packet delivery,
  153. SimpleTcp agent simplifies the trace files 
  154. and hence aids the debugging of application protocols, which, in our case, 
  155. is the web cache consistency protocol.}
  156. (Currently TcpApp doesn't support asymmetric TCP agents, i.e., sender is
  157. separated from receiver). It provides the following OTcl interfaces:
  158. begin{itemize}
  159. item code{connect}: Connecting another TcpApp to this one. This
  160.   connection is bi-directional, i.e., only one call to code{connect} is 
  161.   needed, and data can be sent in either direction. 
  162. item code{send}: It takes two arguments: code{(nbytes, str)}.
  163.   code{nbytes} is the ``nominal'' size of application data. code{str} 
  164.   is application data in string form.
  165. end{itemize}
  166. In order to send application data in binary form, TcpApp provides a 
  167. virtual C++ method code{send(int nbytes, int dsize, const char *data)}.
  168. In fact, this is the method used to implement the OTcl method code{send}.
  169. Because it's difficult to deal with binary data in Tcl, no OTcl interface
  170. is provided to handle binary data. code{nbytes} is the number of bytes 
  171. to be transmitted, code{dsize} is the actual size of the array code{data}.
  172. TcpApp provides a C++ virtual method code{process_data(int size, char*data)}
  173. to handle the received data. The default handling is to treat the data 
  174. as a tcl script and evaluate the script. But it's easy to derive a class
  175. to provide other types of handling.
  176. Here is an example of using Application/TcpApp. A similar example is 
  177. code{Test/TcpApp-2node} in ns/tcl/test/test-suite-webcache.tcl.
  178. First, we create FullTcp agents and connect them:
  179. begin{program}
  180.         set tcp1 [new Agent/TCP/FullTcp]
  181.         set tcp2 [new Agent/TCP/FullTcp]
  182.         # {cf Set TCP parameters here, e.g., window_, iss_, ldots}
  183.         $ns attach-agent $n1 $tcp1
  184.         $ns attach-agent $n2 $tcp2
  185.         $ns connect $tcp1 $tcp2
  186.         $tcp2 listen
  187. end{program}
  188. Then we create TcpApps and connect them:
  189. begin{program}
  190.         set app1 [new Application/TcpApp $tcp1]
  191.         set app2 [new Application/TcpApp $tcp2]
  192.         $app1 connect $app2
  193. end{program}
  194. Now we let code{$app1} %$
  195. be sender and code{$app2} %$ 
  196. be receiver:
  197. begin{program}
  198.         $ns at 1.0 "$app1 send 100 bs"$app2 app-recv 100bs""
  199. end{program} %$
  200. Where code{app-recv} is defined as:
  201. begin{program}
  202.         Application/TcpApp instproc app-recv { size } {
  203.                 global ns
  204.                 puts "[$ns now] app2 receives data $size from app1"
  205.         }
  206. end{program}
  207. subsection{Class hierarchy related to user data handling}
  208. We conclude this section by providing a hierarchy of classes involved
  209. in this section (Figure ref{fig:appdata-hier}).
  210. begin{figure}[tb]
  211.   begin{center}
  212.     includegraphics{appdata-hier}
  213.     caption{Hierarchy of classes related to application-level data handling}
  214.     label{fig:appdata-hier}
  215.   end{center}
  216. end{figure}
  217. section{Overview of web cache classes}
  218. label{sec:webcache-class}
  219. There are three major classes related to web cache, as it is in the
  220. real world: client (browser), server, and cache. Because they share a
  221. common feature, i.e., the HTTP protocol, they are derived from the
  222. same base class code{Http} (Name of OTcl class, it's called
  223. code{HttpApp} in C++). For the following reasons, it's not a real
  224. Application.  First, an HTTP object (i.e., client/cache/server) may
  225. want to maintain multiple concurrent HTTP connections, but an
  226. Application contains only one code{agent_}.  Also, an HTTP object
  227. needs to transmit real data (e.g., HTTP header) and that's provided by
  228. TcpApp instead of any Agent. Therefore, we choose to use a standalone
  229. class derived from TclObject for common features of all HTTP objects,
  230. which are managing HTTP connections and a set of pages.  In the rest
  231. of the section, we'll discuss these functionalities of Http. In the
  232. next three sections, we'll in turn describe HTTP client, cache and
  233. server.
  234. subsection{Managing HTTP connections}
  235. label{sec:webcache-connection}
  236. Every HTTP connection is embodied as a TcpApp
  237. object. Http maintains a hash of TcpApp objects, which are all of 
  238. its active connections. It assumes that to any other Http, it 
  239. has only one HTTP connection. It also allows dynamic establishment and 
  240. teardown of connections. Only OTcl interface is provided for establishing,
  241. tearing down a connection and sending data through a connection.
  242. paragraph{OTcl methods}
  243. Following is a list of OTcl interfaces related to connection management 
  244. in Http objects:
  245. begin{alist}
  246. id & return the id of the Http object, which is the id of the node the object
  247. is attached to. \
  248. get-cnc tup{client} & return the TCP agent associated with $client (Http object).\
  249. is-connected tup{server} & return 0 if not connected to $server, 1 otherwise.\
  250. send tup{client} tup{bytes} tup{callback} & send $bytes of data to 
  251. $client. When it's done, execute $callback (a OTcl command). \
  252. connect tup{client} tup{TCP} & associate a TCP agent with $client (Http object). That agent will be used to send packets emph{to} $client. \
  253. disconnect tup{client} & delete the association of a TCP agent with $client.
  254. Note that neither the TCP agent nor $client is not deleted, only the 
  255. association is deleted.\
  256. end{alist}
  257. paragraph{Configuration parameter}
  258. By default, Http objects use Agent/SimpleTcp as transport agents
  259. (section ref{sec:webcache-tcpapp}). They can also use Agent/FullTcp
  260. agents, which allows Http objects to operate in a lossy network.
  261. Class variable code{TRANSPORT_} is used for this purpose. E.g.,
  262. code{Http set TRANSPORT_ FullTcp} tells all Http objects use
  263. FullTcp agents.
  264. This configuration should be done emph{before} simulation starts, and 
  265. it should not change during simulation, because FullTcp agents do not 
  266. inter-operate with SimpleTcp agents.
  267. subsection{Managing web pages}
  268. label{sec:webcache-page}
  269. Http also provides OTcl interfaces to manage a set of pages. The 
  270. real management of pages are handled by class code{PagePool} and its
  271. subclasses. Because different HTTP objects have different requirements
  272. for page management, we allow different PagePool subclasses to be attached
  273. to different subclasses of Http class. Meanwhile, we export
  274. a common set of PagePool interfaces to OTcl through
  275. Http. For example, a browser may use a PagePool only to generate a 
  276. request stream, so its PagePool only needs to contain a list of URLs. But
  277. a cache may want to store page size, last modification time of every page 
  278. instead of a list of URLs. However, this separation is not clearcut in 
  279. the current implementation. 
  280. Page URLs are represented in the form of:
  281. code{tup{ServerName}:tup{SequenceNumber}}
  282. where the {tt ServerName} is the name of OTcl object, and 
  283. every page in every server should have a unique {tt SequenceNumber}. 
  284. Page contents are ignored. Instead, every page contains several 
  285. emph{attributes}, which are represented in OTcl as a list of the following 
  286. (tup{name} tup{value}) pairs: ``modtime tup{val}'' (page 
  287. modification time), ``size tup{val}'' (page size), and ``age tup{val}''}
  288. The ordering of these pairs is not significant.
  289. Following is a list of related OTcl methods.
  290. begin{alist}
  291. set-pagepool tup{pagepool} & set page pool \
  292. enter-page tup{pageid} tup{attributes} & add a page with id $pageid
  293. into pool. $attributes is the attributes of $pageid, as described above. \
  294. get-page tup{pageid} & return page attributes in the format described 
  295. above. \
  296. get-modtime tup{pageid} & return the last modification time of the page 
  297. $pageid. \
  298. exist-page tup{pageid} & return 0 if $pageid doesn't exist in this 
  299. Http object, 1 otherwise. \
  300. get-size tup{pageid} & return the size of $pageid. \
  301. get-cachetime tup{pageid} & return the time when page $pageid is entered
  302. into the cache. \
  303. end{alist}
  304. subsection{Debugging}
  305. label{sec:webcache-debug}
  306. HttpApp provides two debugging methods. code{log} registers a file 
  307. handle as the trace file for all HttpApp-specific traces. Its trace format 
  308. is described in section ref{sec:webcache-trace}. code{evTrace} logs a 
  309. particular event into trace file. It concatenates
  310. time and the id of the HttpApp to the given string, and writes it out. 
  311. Details can be found in ns/webcache/http.cc.
  312. section{Representing web pages}
  313. We represent web pages as the abstract class Page. It is defined as follows:
  314. begin{program}
  315. class Page {
  316. public:
  317.         Page(int size) : size_(size) {}
  318.         int size() const { return size_; }
  319.         int& id() { return id_; }
  320.         virtual WebPageType type() const = 0;
  321. protected:
  322.         int size_;
  323.         int id_;
  324. };
  325. end{program}
  326. It represents the basic properties of a web page: size and URL. Upon
  327. it we derive two classes of web pages: ServerPage and ClientPage. The
  328. former contains a list of page modification times, and is supposed to
  329. by used by servers. It was originally designed to work with a special
  330. web server trace; currently it is not widely used in ns. The latter,
  331. ClientPage, is the default web page for all page pools below. 
  332. A ClientPage has the following major properties (we omit some
  333. variables used by web cache with invalidation, which has too many
  334. details to be covered here):
  335. begin{itemize}
  336. item code{HttpApp* server_} - Pointer to the original server of this
  337.   page. 
  338. item code{double age_} - Lifetime of the page.
  339. item code{int status_} - Status of the page. Its contents are
  340.   explained below.
  341. end{itemize}
  342. The status (32-bit) of a ClientPage is separated into two 16-bit
  343. parts. The first part (with mask 0x00FF) is used to store page
  344. status, the second part (with mask 0xFF00) is used to store expected
  345. page actions to be performed by cache. Available page status are (again,
  346. we omit those closely related to web cache invalidation):
  347. begin{alist}
  348. HTTP_VALID_PAGE & Page is valid. \
  349. HTTP_UNCACHEABLE & Page is uncacheable. This option can be used to
  350. simulate CGI pages or dynamic server pages. \
  351. end{alist}
  352. CilentPage has the following major C++ methods: 
  353. begin{itemize}
  354. item code{type()} - Returns the type of the page. Assuming pages of
  355.   the same type should have identical operations, we let all
  356.   ClientPage to be of type ``HTML''. If later on other types of web
  357.   pages are needed, a class may be derived from ClientPage (or Page)
  358.   with the desired type. 
  359. item code{name(char *buf)} - Print the page's name into the given
  360.   buffer. A page's name is in the format of:
  361.   tup{ServerName}:tup{PageID}. 
  362. item code{split_name(const char *name, PageID& id)} - Split a given
  363.   page name into its two components. This is a static method. 
  364. item code{mtime()} - Returns the last modification time of the page.
  365. item code{age()} - Returns the lifetime of the page. 
  366. end{itemize}
  367. section{Page pools}
  368. label{sec:webcache-pagepool}
  369. PagePool and its derived classes are used by servers to generate page
  370. information (name, size, modification time, lifetime, etc.), by caches
  371. to describe which pages are in storage, and by clients to generate a
  372. request stream. Figure~ref{fig:pagepool-hier} provides an overview of
  373. the class hierarchy here. 
  374. begin{figure}[tb]
  375.   begin{center}
  376.     includegraphics{pagepool-hier}
  377.     caption{Class hierarchy of page pools}
  378.     label{fig:pagepool-hier}
  379.   end{center}
  380. end{figure}
  381. Among these, class PagePool/Client is mostly used by caches to store
  382. pages and other cache-related information; other three classes are
  383. used by servers and clients. In the following we describe these
  384. classes one by one.
  385. subsection{PagePool/Math}
  386. This is the simplest type of page pool. It has only one page, whose
  387. size can be generated by a given random variable. Page modification
  388. sequence and request sequence are 
  389. generated using two given random variables. It has the following OTcl
  390. methods:
  391. begin{alist}
  392. gen-pageid & Returns the page ID which will be requested next. Because
  393.  it has only one page, it always returns 0.\
  394. gen-size & Returns the size of the page. It can be generated by a
  395.   given random variable. \
  396. gen-modtime tup{pageID} tup{mt} & Returns the next modification time of the
  397.   page. tup{mt} gives the last modification time. It uses the
  398.   lifetime random variable. \
  399. ranvar-age tup{rv} & Set the file lifetime random variable as
  400.   tup{rv}. \
  401. ranvar-size tup{rv} & Set the file size random variable to be
  402.   tup{rv}. \
  403. end{alist}
  404. {em NOTE}: There are two ways to generate a request sequence. With
  405. all page pools except PagePool/ProxyTrace, request sequence is
  406. generated with a random variable which describes the request
  407. interval, and the code{gen-pageid} method of other page pools gives
  408. the page ID of the next request. PagePool/ProxyTrace loads the request
  409. stream during initialization phase, so it does not need a random
  410. variable for request interval; see its description below. 
  411. An example of using PagePool/Math is at Section
  412. ref{sec:webcache-example}. That script is also available at 
  413. ns/tcl/ex/simple-webcache.tcl. 
  414. subsection{PagePool/CompMath}
  415. It improves over PagePool/Math by introducing a compound page
  416. model. By a compound page we mean a page which consists of a main text
  417. page and a number of embedded objects, e.g., GIFs. We model a compound
  418. page as a main page and several component objects. The main page is
  419. always assigned with ID 0. All component pages
  420. have the same size; both the main page size and component object size is
  421. fixed, but adjustable through OTcl-bound variables code{main_size_}
  422. and code{comp_size_}, respectively. The number of component objects
  423. can be set using the OTcl-bound variable code{num_pages_}.
  424. PagePool/CompMath has the following major OTcl methods:
  425. begin{alist}
  426. gen-size tup{pageID} & If tup{pageID} is 0, return
  427.   code{main_size_}, otherwise return code{comp_size_}.\
  428. ranvar-main-age tup{rv} & Set random variable for main page
  429.   lifetime. Another one, code{ranvar-obj-age}, set that for component
  430.   objects. \
  431. gen-pageid & Always returns 0, which is the main page ID. \ 
  432. gen-modtime tup{pageID} tup{mt} & Returns the next modification time
  433.   of the given page tup{pageID}. If the given ID is 0, it uses the
  434.   main page lifetime random variable; otherwise it uses the component
  435.   object lifetime random variable. \
  436. end{alist}
  437. An example of using PagePool/CompMath is available at 
  438. ns/tcl/ex/simple-webcache-comp.tcl.
  439. subsection{PagePool/ProxyTrace}
  440. The above two page pool synthesize request stream to a single web page
  441. by two random variables: one for request interval, another for
  442. requested page ID. Sometimes users may want more complicated request
  443. stream, which consists of multiple pages and exhibits spatial locality
  444. and temporal locality. There exists one proposal (SURGE
  445. cite{Barf98:WebWorkload}) 
  446. which generates such request streams, we choose to provide an
  447. alternative solution: use real web proxy cache trace (or server
  448. trace). 
  449. The class PagePool/ProxyTrace uses real traces to drive
  450. simulation. Because there exist many web traces with different
  451. formats, they should be converted into a intermediate format before
  452. fed into this page pool. The converter is available at 
  453. http://mash.cs.berkeley.edu/dist/vint/webcache-trace-conv.tar.gz.
  454. It accepts four trace formats: DEC proxy trace (1996), UCB
  455. Home-IP trace, NLANR proxy trace, and EPA web server trace. It
  456. converts a given trace into two files: pglog and reqlog. Each line in
  457. pglog has the following format:
  458. begin{center}
  459. begin{verbatim}
  460. [<serverID> <URL_ID> <PageSize> <AccessCount>]
  461. end{verbatim}
  462. end{center}
  463. Each line, except the last line, in reqlog has the following format:
  464. begin{center}
  465. begin{verbatim}
  466. [<time> <clientID> <serverID> <URL_ID>]
  467. end{verbatim}
  468. end{center}
  469. The last line in reqlog records the duration of the entire trace and
  470. the total number of unique URLs:
  471. begin{center}
  472. begin{verbatim}
  473. i <Duration> <Number_of_URL>
  474. end{verbatim}
  475. end{center}
  476. PagePool/ProxyTrace takes these two file as input, and use them to
  477. drive simulation. Because most existing web proxy traces do not
  478. contain complete page modification information, we choose to use a
  479. bimodal page modification model cite{Cao97:CacheConsistency}. We
  480. allow user to select $x%$ of the pages to have one random page
  481. modification interval generator, and the rest of the pages to have
  482. another generator. In this way, it's possible to let $x%$ pages to be
  483. dynamic, i.e., modified frequently, and the rest static. Hot pages are
  484. evenly distributed among all pages. For example, assume 10% pages are
  485. dynamic, then if we sort pages into a list according to their popularity,
  486. then pages 0, 10, 20, $ldots$ are dynamic, rest are static. Because
  487. of this selection mechanism, we only allow bimodal ratio to change in
  488. the unit of 10%. 
  489. In order to distribute requests to different requestors in the
  490. simulator, PagePool/ProxyTrace maps the client ID in the traces to
  491. requestors in the simulator using a modulo operation. 
  492. PagePool/ProxyTrace has the following major OTcl methods:
  493. begin{alist}
  494. get-poolsize & Returns the total number of pages. \
  495. get-duration & Returns the duration of the trace. \
  496. bimodal-ratio & Returns the bimodal ratio. \
  497. set-client-num tup{num} & Set the number of requestors in the
  498. simulation. \
  499. gen-request tup{ClientID} & Generate the next request for the given
  500. requestor. \
  501. gen-size tup{PageID} & Returns the size of the given page. \
  502. bimodal-ratio tup{ratio} & Set the dynamic pages to be tup{ratio}*10
  503. percent. Note that this ratio changes in unit of 10%. \
  504. ranvar-dp tup{ranvar} & Set page modification interval generator for 
  505. dynamic pages. Similarly, ranvar-sp tup{ranvar} sets the generator
  506. for static pages. \
  507. set-reqfile tup{file} & Set request stream file, as discussed
  508. above. \
  509. set-pgfile tup{file} & Set page information file, as discussed
  510. above. \
  511. gen-modtime tup{PageID} tup{LastModTime} & Generate next
  512. modification time for the given page. \
  513. end{alist}
  514. An example of using PagePool/ProxyTrace is available at 
  515. ns/tcl/ex/simple-webcache-trace.tcl. 
  516. subsection{PagePool/Client}
  517. The class PagePool/Client helps caches to keep track of pages resident
  518. in cache, and to store various cache-related information about
  519. pages. It is mostly implemented in C++, because it is mainly used
  520. internally and little functionality is needed by users. It has the
  521. following major C++ methods:
  522. begin{itemize}
  523. item code{get_page(const char* name)} - Returns a pointer to the
  524.   page with the given name. 
  525. item code{add_page(const char *name, int size, double mt, double et, double age)} - Add a page with given size, last modification
  526.     time (mt), cache entry time (et), and page lifetime (age). 
  527. item code{remove_page(const char* name)} - Remove a page from cache.
  528. end{itemize}
  529. This page pool should support various cache replacement algorithms,
  530. however, it has not been implemented yet. 
  531. subsection{PagePool/WebTraf}
  532. The class PagePool/WebTraf is a standalone Web traffic modle that utilizes
  533. PagePool framework. However, this class has nothing to do with the HttpApp 
  534. classes. Because we are only interested in using it to study Web traffic 
  535. pattern here, and do not want to be bothered with the burden of 
  536. transmitting HTTP headers, etc. It has the following two major data structures.
  537. Details can be found in ns/webcache/webtraf.cc and ns/webcache/webtraf.h, the
  538. architecture WebTraf model is also loosely described in~cite{Feldmann99a}, 
  539. Section 2.4, paragraph 3-4 and the appendix A.1.
  540. begin{itemize}
  541. item code{WebTrafSession} - a class that models Web user session. It is defined as follows:
  542. begin{program}
  543. class WebTrafSession : public TimerHandler {
  544. public:   
  545.         WebTrafSession(WebTrafPool *mgr, Node *src, int np, int id) : rvInterPage_(NULL),
  546.         rvPageSize_(NULL), rvInterObj_(NULL), rvObjSize_(NULL), mgr_(mgr), src_(src), 
  547.         nPage_(np), curPage_(0), donePage_(0), id_(id), interPageOption_(1) {} 
  548.         virtual ~WebTrafSession();
  549.         // Queried by individual pages/objects
  550.         inline RandomVariable*& interPage() { return rvInterPage_; }
  551.         inline RandomVariable*& pageSize() { return rvPageSize_; }
  552.         inline RandomVariable*& interObj() { return rvInterObj_; }
  553.         inline RandomVariable*& objSize() { return rvObjSize_; }
  554.         void donePage(void* ClntData); // all the pages within this
  555.                                        // session have  been sent
  556.         void launchReq(void* ClntData, int obj, int size);
  557.         inline int id() const { return id_; }
  558.         inline WebTrafPool* mgr() { return mgr_; }
  559.  private:
  560.         virtual void expire(Event *e = 0); // Lanuch request for a page
  561.         virtual void handle(Event *e); // schedule the timer for next page
  562.         RandomVariable *rvInterPage_, *rvPageSize_, *rvInterObj_, *rvObjSize_;
  563.         WebTrafPool* mgr_;
  564.         Node* src_;  // One Web client (source of request) per session
  565.         nt nPage_; // number of pages per session
  566.         int curPage_; // number of pages that have been sent
  567.         int id_; // page ID
  568.         int interPageOption_;
  569. }
  570. end{program}
  571. item code{WebPage} - a class that models Web Page. It is defined as follows:
  572. begin{program}
  573. class WebPage : public TimerHandler {
  574. public:
  575.         WebPage(int id, WebTrafSession* sess, int nObj, Node* dst) :
  576.                 id_(id), sess_(sess), nObj_(nObj), curObj_(0),
  577.                 doneObj_(0), dst_(dst) {}
  578.         virtual ~WebPage() {}
  579.         inline void start() { // Call expire() and schedule the next one if needed     
  580.         void doneObject() { // All the objects within this page have been sent
  581.         inline int id() const { return id_; }
  582.         Node* dst() { return dst_; }
  583.         inline int curObj() const { return curObj_; }
  584.         inline int doneObj() const { return doneObj_; }
  585. private:  
  586.         virtual void expire(Event* = 0) { // Launch request for an object
  587.         virtual void handle(Event *e) { // schedule the timer for the next object
  588.         int id_; // object ID
  589.         WebTrafSession* sess_; // the session that requested this page
  590.         int nObj_; // number of object in this page
  591.         int curObj_ ; // number of object that have been sent
  592.         Node* dst_; //  server that this page has been requested from
  593. }
  594. end{program}
  595. end{itemize}
  596. Following is a list of related OTcl methods to the WebTraf class.
  597. begin{alist}
  598. set-num-session tup{number-of-session} & set the total number of sessions in the WebTraf pool. \
  599. set-num-server tup{number-of-server} & set the total number of servers. \
  600. set-num-client tup{number-of-client} & set the total number clients. \
  601. set-interPageOption tup{option} & There are two ways to interpret emph{inter-page} time: One is the time between the start of two consecutive page downloads by the same user, and the other is the time between the end of previous page download and the start of the following page by the same user.  $option can be set 
  602. to either 0 or 1 (default is 1). When $option is set to 1, the second interpretation is used for "inter-page" time. The first interpratation is adopted when $option is set to 0. Note the resulted traffic volume using the first interpretation is much higher than the second interpretation. \
  603. doneObj tup{webpage} & all the objects in $webpage have been sent. \
  604. set-server tup{id} tup{node} & set $node as server $id. \
  605. set-client tup{id} tup{node} & set $node as client $id. \
  606. recycle tup{tcp} tup{sink} & Recycle a TCP source/sink pair. \
  607. create-session tup{session-index} tup{pages-per-sess} & \
  608.  tup{launch-time} tup{inter-page-rv} tup{page-size-rv} & \
  609.  tup{inter-obj-rv} tup{obj-size-rv} & 
  610. Create a Web session. $session-index is the sesson index. $pages-per-sess is
  611. the total number of pages per session. $launch-time is session starting time. 
  612. $inter-page-rv is the random variable that generates page inter-arrival time.
  613. $page-size-rv is the random variable that generates number of objects per page.
  614. $inter-obj-rv is the random variable that generates object inter-arrival time.
  615. $obj-size-rv is the random variable that generates object size. \
  616. end{alist}
  617. The example script is available at ns/tcl/ex/web-traffic.tcl (also
  618. see ns/tcl/ex/large-scale-web-traffic.tcl for use of a large-scale web traffic 
  619. simulation)
  620. section{Web client}
  621. label{sec:webcache-client}
  622. Class Http/Client models behavior of a simple web browser. It
  623. generates a sequence of page requests, where request interval and page 
  624. IDs are randomized. It's a pure OTcl class inherited from Http. 
  625. Next we'll walk through its functionalities and usage.
  626. paragraph{Creating a client}
  627. First of all, we create a client and connect it to a cache and a web server.
  628. Currently a client is only allowed to connect to a single cache, but it's 
  629. allowed to connect to multiple servers. Note that this has to be called 
  630. emph{AFTER} the simulation starts (i.e., after code{$ns run} %$
  631. is called).
  632. This remains true for all of the following methods and code examples of 
  633. Http and its derived classes, unless explicitly said.
  634. begin{program}
  635.         # Assuming $server is a configured Http/Server. 
  636.         set client [new Http/Client $ns $node] ; client resides on this node;
  637.         $client connect $server ; connecting client to server;
  638. end{program} %$
  639. paragraph{Configuring request generation}
  640. For every request, Http/Client uses PagePool to generate a random page
  641. ID, and use a random variable to generate intervals between two 
  642. consecutive requests:
  643. footnote{Some PagePool,
  644. e.g., PagePool/Math, has only one page and therefore it always returns the
  645. same page. Some other PagePool, e.g. PagePool/Trace, has multiple pages 
  646. and needs a random variable to pick out a random page.} 
  647. begin{program}
  648.         $client set-page-generator $pgp ; attach a configured PagePool;
  649.         $client set-interval-generator $ranvar ; attach a random variable;
  650. end{program}
  651. Here we assume that PagePools of Http/Client share the same set of pages
  652. as PagePools of the server. Usually we simplify our simulation by letting
  653. all clients and servers share the same PagePool, i.e., they have the same
  654. set of pages. When there are multiple servers, or servers' PagePools 
  655. are separated from those of clients', care must be taken to make sure that 
  656. every client sees the same set of pages as the servers to which they are
  657. attached.
  658. paragraph{Starting}
  659. After the above setup, starting requests is very simple:
  660. begin{program}
  661.         $client start-session $cache $server ; assuming $cache is a configured Http/Cache;
  662. end{program}
  663. paragraph{OTcl interfaces}
  664. Following is a list of its OTcl methods (in addition to those
  665. inherited from Http). This is not a complete list. More details can be
  666. found in ns/tcl/webcache/http-agent.tcl.
  667. begin{alist}
  668. send-request tup{server} tup{type} tup{pageid} tup{args} & 
  669. send a request of page $pageid and type $type to $server. The only 
  670. request type allowed for a client is GET. $args has a format identical
  671. to that of $attributes described in code{Http::enter-page}. \
  672. start-session tup{cache} tup{server} & start sending requests of a 
  673. random page to $server via $cache. \
  674. start tup{cache} tup{server} & before sending requests, populate
  675. $cache with all pages in the client's PagePool. This method is useful 
  676. when assuming infinite-sized caches and we want to observe behaviors 
  677. of cache consistency algorithms in steady state. \
  678. set-page-generator tup{pagepool} & attach a PagePool to generate 
  679. random page IDs.\
  680. set-interval-generator tup{ranvar} & attach a random variable to generate
  681. random request intervals.\
  682. end{alist}
  683. section{Web server}
  684. label{seccom:webcache-server}
  685. Class Http/Server models behavior of a HTTP server. Its
  686. configuration is very simple. All that a user needs to do is to create 
  687. a server, attach a PagePool and wait:
  688. begin{program}
  689.         set server [new Http/Server $ns $node] ; attach $server to $node;
  690.         $server set-page-generator $pgp ; attach a page pool;
  691. end{program}
  692. An Http/Server object waits for incoming requests after simulation starts.
  693. Usually clients and caches initiates connection to an Http/Server. But 
  694. it still has its own code{connect} method, which allows an Http/Server 
  695. object to actively connect to a certain cache (or client). Sometimes this
  696. is useful, as explained in Test/TLC1::set-groups{} in 
  697. ns/tcl/test/test-suite-webcache.tcl.
  698. An Http/Server object accepts two types of requests: GET and IMS.  GET
  699. request models normal client requests. For every GET request, it
  700. returns the attributes of the requested page.  IMS request models
  701. If-Modified-Since used by TTL algorithms for cache consistency. For
  702. every IMS (If-Modified-Since) request, it compares the page
  703. modification time given in the request and that of the page in its
  704. PagePool. If the time indicated in the request is older, it sends back
  705. a response with very small size, otherwise it returns all of the page 
  706. attributes with response size equal the real page size.
  707. section{Web cache}
  708. label{sec:webcache-cache}
  709. Currently 6 types of web caches are implemented, including 
  710. the base class Http/Cache. Its five derived subclasses 
  711. implement 5 types of cache consistency algorithms: Plain old TTL, 
  712. adaptive TTL, Omniscient TTL, Hierarchical multicast invalidation, 
  713. and hierarchical multicast invalidation plus direct request.
  714. In the following we'll only describe the base class Http/Cache, because 
  715. all the subclasses involves discussion of cache consistency algorithms 
  716. and it does not seem to be appropriate here.
  717. subsection{Http/Cache}
  718. label{sec:webcache-cache-base}
  719. Class Http/Cache models behavior of a simple HTTP cache with infinite 
  720. size. It doesn't contain removal algorithm, nor consistency algorithm. 
  721. It is not intended to be used by itself. Rather, it is meant to be a 
  722. base class for experimenting with various cache consistency algorithms and
  723. other cache algorithms. 
  724. paragraph{Creation and startup}
  725. Creating an Http/Cache requires the same set of parameters as
  726. Http/Client and Http/Server. After creation, a cache needs to connect 
  727. to a certain server. Note that this creation can also be done dynamically,
  728. when a request comes in and the cache finds that it's not connected to 
  729. the server. However, we do not model this behavior in current code.
  730. Following code is an example:
  731. begin{program}
  732.         set cache [new HttpCache $ns $node] ; attach cache to $node;
  733.         $cache connect $server ; connect to $server;
  734. end{program}
  735. Like Http/Server, an Http/Cache object waits for requests (and packets
  736. from server) after it's initialized as above. When hierarchical
  737. caching is used, the following can be used to create the hierarchy:
  738. begin{program}
  739.         $cache set-parent $parent ; set parent cache;
  740. end{program}
  741. Currently all TTL and multicast invalidation caches support hierarchical
  742. caching. However, only the two multicast invalidation caches allows 
  743. multiple cache hierarchies to inter-operate.
  744. paragraph{OTcl methods}
  745. Although Http/Cache is a SplitObject, all of its methods are in OTcl. 
  746. Most of them are used to process an incoming request. Their relations can
  747. be illustrated with the flowchart below, followed by explainations:
  748. begin{figure}[ht]
  749.   begin{center}
  750.     includegraphics{cache-flowchart}
  751.     caption{Handling of incoming request in Http/Cache}
  752.     label{fig:webcache-handle-request}
  753.   end{center}
  754. end{figure}
  755. begin{alist}
  756. get-request tup{client} tup{type} tup{pageid} & 
  757. The entry point of processing any request. It checks if the requested 
  758. page $pageid exists in the cache's page pool, then call either 
  759. code{cache-hit} or code{cache-miss}. \
  760. cache-miss tup{client} tup{type} tup{pageid} & 
  761. This cache doesn't have the page. Send a request to server (or parent 
  762. cache) to refetch the page if it hasn't already done so. Register 
  763. $client in a list so that when the cache gets the page, it'll forward
  764. the page to all clients who have requested the page. \
  765. cache-hit tup{client} tup{type} tup{pageid} &
  766. Checks the validatity of the cached page. If it's valid, send $client
  767. the cached page, otherwise refetch the page. \ 
  768. is-consistent tup{client} tup{type} tup{pageid} & 
  769. Returns 1 if $pageid is valid. This is intended to be overridden by 
  770. subclasses. \
  771. refetch tup{client} tup{type} tup{pageid} & 
  772. Refetch an invalid page from server. This is intended to be overridden 
  773. by subclasses. \
  774. end{alist}
  775. section{Putting together: a simple example}
  776. label{sec:webcache-example}
  777. We have seen all the pieces, now we present a script which provides a
  778. complete view of all pieces together. First, we build topology and 
  779. other usual initializations:
  780. begin{program}
  781.         set ns [new Simulator]
  782.         # Create topology/routing
  783.         set node(c) [$ns node] 
  784.         set node(e) [$ns node]
  785.         set node(s) [$ns node]
  786.         $ns duplex-link $node(s) $node(e) 1.5Mb 50ms DropTail
  787.         $ns duplex-link $node(e) $node(c) 10Mb 2ms DropTail 
  788.         $ns rtproto Session
  789. end{program}
  790. Next we create the Http objects:
  791. begin{program}
  792.         # HTTP logs
  793.         set log [open "http.log" w]
  794.         # Create page pool as a central page generator. Use PagePool/Math
  795.         set pgp [new PagePool/Math]
  796.         set tmp [new RandomVariable/Constant] ;# Page size generator;
  797.         $tmp set val_ 1024  ;# average page size;
  798.         $pgp ranvar-size $tmp
  799.         set tmp [new RandomVariable/Exponential] ;# Page age generator;
  800.         $tmp set avg_ 5 ;# average page age;
  801.         $pgp ranvar-age $tmp
  802.         set server [new Http/Server $ns $node(s)] ;# Create a server and link it to the central page pool;
  803.         $server set-page-generator $pgp
  804.         $server log $log
  805.         set cache [new Http/Cache $ns $node(e)] ;# Create a cache;
  806.         $cache log $log
  807.         set client [new Http/Client $ns $node(c)] ;# Create a client;
  808.         set tmp [new RandomVariable/Exponential] ;# Poisson process as request sequence;
  809.         $tmp set avg_ 5 ;# average request interval;
  810.         $client set-interval-generator $tmp
  811.         $client set-page-generator $pgp
  812.         $client log $log
  813.         set startTime 1 ;# simulation start time;
  814.         set finishTime 50 ;# simulation end time;
  815.         $ns at $startTime "start-connection"
  816.         $ns at $finishTime "finish"
  817. end{program} %$
  818. Then we define a procedure which will be called after simulation
  819. starts.  The procedure will setup connections among all Http objects.
  820. begin{program}
  821.         proc start-connection {} {
  822.                 global ns server cache client
  823.                 $client connect $cache
  824.                 $cache connect $server
  825.                 $client start-session $cache $server
  826.         }
  827. end{program} %$
  828. At the end, the usual closing:
  829. begin{program}
  830.         proc finish {} {
  831.                 global ns log
  832.                 $ns flush-trace
  833.                 flush $log
  834.                 close $log
  835.                 exit 0
  836.         }
  837.         $ns run
  838. end{program}
  839. This script is also available at ns/tcl/ex/simple-webcache.tcl. 
  840. Examining its output code{http.log}, one will find that the result of 
  841. the absense cache consistency algorithm results in a lot of stale hits. 
  842. This can be easily remedied by replacing ``new Http/Cache'' line with:
  843. code{set cache [new Http/Cache/TTL $ns $node(e)]}. For more complicated
  844. cache consistency algorithm examples, see 
  845. ns/tcl/test/test-suite-webcache.tcl.
  846. section{Http trace format}
  847. label{sec:webcache-trace}
  848. The trace file of Http agents are constructed in a similar way as the
  849. SRM trace files. It consists of multiple entries, each of which
  850. occupies one line.  The format of each entry is:
  851. begin{tabular}[h]{c|c|c}
  852.   Time & ObjectID & Object Values
  853. end{tabular}
  854. There are three types of objects: client ({bf C}), cache ({bf E})
  855. and server ({bf S}). Following is a complete enumeration of all possible 
  856. events and value types associated with these three types of objects.
  857. begin{center}
  858.   begin{tabular}[h]{c|c|l}
  859.     emph{Object Type} & emph{Event Type} & emph{Values} \ hline
  860.     E & HIT & tup{Prefix} \
  861.     E & MISS & tup{Prefix} z tup{RequestSize} \
  862.     E & IMS & tup{Prefix} z tup{Size} t tup{CacheEntryTime} \
  863.     E & REF & p tup{PageID} s tup{ServerID} z tup{Size} \
  864.     E & UPD & p tup{PageID} m tup{LastModifiedTime} z tup{PageSize} \
  865.     &     & s tup{ServerID} \ 
  866.     E & GUPD & z tup{PageSize} \
  867.     E & SINV & p tup{PageID} m tup{LastModTime} z tup{PageSize} \
  868.     E & GINV & p tup{PageID} m tup{LastModTime} \
  869.     E & SPF & p tup{PageID} c tup{DestCache} \
  870.     E & RPF & p tup{PageID} c tup{SrcCache} \
  871.     E & ENT & p tup{PageID} m tup{LastModifiedTime} z tup{PageSize} \
  872.     &     & s tup{ServerID} \ hline
  873.     C & GET & p tup{PageID} s tup{PageServerID} z tup{RequestSize}\
  874.     C & STA & p tup{PageID} s tup{OrigServerID} l tup{StaleTime}\
  875.     C & RCV & p tup{PageID} s tup{PageServerID} l tup{ResponseTime} z tup{PageSize}\ hline
  876.     S & INV & p tup{PageID} m tup{LastModifiedTime} z tup{Size} \
  877.     S & UPD & p tup{PageID} m tup{LastModifiedTime} z tup{Size} \
  878.     S & SND & p tup{PageID} m tup{LastModifiedTime} z tup{PageSize} \
  879.       &     & t tup{Requesttype} \
  880.     S & MOD & p tup{PageID} n tup{NextModifyTime} \
  881.   end{tabular}
  882. end{center}
  883. tup{Prefix} is the information common to all trace entries. It includes:
  884. begin{center}
  885.   begin{tabular}[h]{c|c|c}
  886.     p tup{PageID} & c tup{RequestClientID} & s tup{PageServerID}
  887.   end{tabular}
  888. end{center}
  889. emph{Short Explaination of event operations}: 
  890. begin{center}
  891.   begin{tabular}[h]{c|c|l}
  892.     emph{Object Type} & emph{Event Type} & emph{Explaination} \ hline
  893.     E & HIT & Cache hit. PageSererID is the id of the ``owner'' of the page. \
  894.     E & MISS & Cache miss. In this case the cache will send a request to the
  895.     server to fetch the page. \
  896.     E & IMS & If-Modified-Since. Used by TTL procotols to validate an expired 
  897.     page. \
  898.     E & REF & Page refetch. Used by invalidation protocols to refetch an 
  899.     invalidated page. \
  900.     E & UPD & Page update. Used by invalidation protocols to ``push'' updates\
  901.       & & from parent cache to children caches. \
  902.     E & SINV & Send invalidation. \
  903.     E & GINV & Get invalidation. \
  904.     E & SPF & Send a pro forma \
  905.     E & RPF & Receive a pro forma \
  906.     E & ENT & Enter a page into local page cache. \ 
  907.     hline
  908.     C & GET & Client sends a request for a page. \
  909.     C & STA & Client gets a stale hit. OrigModTime is the modification time \
  910.     & & in the web server, CurrModTime is the local page's modification time.\
  911.     C & RCV & Client receives a page. \
  912.     hline
  913.     S & SND & Server send a response. \
  914.     S & UPD & Server pushes a page update to its ``primary cache''. Used by
  915.     invalidation protocol only. \
  916.     S & INV & Server sends an invalidation message. Used by invalidation 
  917.     protocol only. \
  918.     S & MOD & Server modified a page. The page will be modified next
  919.     at tup{NextModifyTime}. \
  920.   end{tabular}
  921. end{center}
  922. section{Commands at a glance}
  923. label{sec:webcachecommand}
  924. Following are the web cache related commands:
  925. begin{flushleft}
  926. code{set server [new Http/Server <sim> <s-node>]}\
  927. This creates an instance of an Http server at the specified <s-node>. An
  928. instance of the simulator <sim> needs to be passed as an argument.
  929. code{set client [new Http/Client <sim> <c-node>]}\
  930. This creates an instance of a Http client at the given <c-node>.
  931. code{set cache [new Http/Cache <sim> <e-node>}\
  932. This command creates a cache.
  933. code{set pgp [new PagePool/<type-of-pagepool>]}\
  934. This creates a pagepool of the type specified. The different types of pagepool
  935. currently implemented are:\
  936. PagePool/Math, PagePool/CompMath, PagePool/ProxyTrace and PagePool/Client.
  937. See section ref{sec:webcache-pagepool} for details on Otcl interface for
  938. each type of Pagepool.
  939. code{$server set-page-generator <pgp>}\
  940. code{$server log <handle-to-log-file>}\
  941. The above commands consist of server configuration. First the server is
  942. attached to a central page pool <pgp>. Next it is attached to a log file.
  943. begin{program}
  944. client set-page-generator <pgp>
  945. $client set-interval-generator <ranvar> 
  946. $client log <handle-to-log-file>
  947. end{program}
  948. These consist configuration of the Http client. It is attached to a central
  949. page pool <pgp>. Next a random variable <ranvar> is attached to the client
  950. that is used by it (client) to generate intervals between two consecutive
  951. requests. Lastly the client is attached to a log file for logging its events.
  952. code{$cache log <log-file>}\
  953. This is part of cache configuration that allows the cache to log its
  954. events in a log-file. 
  955. code{$client connect <cache>}\
  956. code{$cache connect <server>}\
  957. Once the client, cache, and server are configured, they need to be
  958. connected as shown in above commands.
  959. code{$client start-session <cache> <server>}\
  960. This starts sending request for a random page from the client to the
  961. <server> via <cache>.
  962. end{flushleft}
  963. endinput
  964. % Local Variables:
  965. % TeX-master: "everything"
  966. % LocalWords:
  967. % End:
  968. % LocalWords:  HTTP tb app dataflow ADU OTcl AppData AppDataType struct hdr buf
  969. % LocalWords:  const AppConnector inline int TclCL HttpApp webcache http nbytes
  970. % LocalWords:  userdata HttpInvalAgent inteded inval recv realsize inv FullTcp
  971. % LocalWords:  SimpleTcp bi str dsize Tcl tcl tcp iss ns TcpApps Mb ms DropTail
  972. % LocalWords:  tmp val RandomVariable pgp ranvar avg startTime finishTime proc