/* * Copyright (c) Xerox Corporation 1998. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Linking this file statically or dynamically with other modules is making * a combined work based on this file. Thus, the terms and conditions of * the GNU General Public License cover the whole combination. * * In addition, as a special exception, the copyright holders of this file * give you permission to combine this file with free software programs or * libraries that are released under the GNU LGPL and with code included in * the standard release of ns-2 under the Apache 2.0 license or under * otherwise-compatible licenses with advertising requirements (or modified * versions of such code, with unchanged license). You may copy and * distribute such a system following the terms of the GNU GPL for this * file and the licenses of the other code concerned, provided that you * include the source code of that other code when and as the GNU GPL * requires distribution of source code. * * Note that people who make modified versions of this file are not * obligated to grant this special exception for their modified versions; * it is their choice whether to do so. The GNU General Public License * gives permission to release a modified version without this exception; * this exception also makes it possible to release a modified version * which carries forward this exception. * * $Header: /nfs/jade/vint/CVSROOT/ns-2/webcache/http-aux.h,v 1.17 2005/08/26 05:05:31 tomh Exp $ */ // // Auxiliary classes for HTTP multicast invalidation proxy cache // #ifndef ns_http_aux_h #define ns_http_aux_h #include #include "random.h" #include "ns-process.h" #include "pagepool.h" #include "timer-handler.h" const int HTTP_HBEXPIRE_COUNT = 5; // How many lost HBs mean disconnection? const int HTTP_HBINTERVAL = 60;// Heartbeat intervals (seconds) const int HTTP_UPDATEINTERVAL = 5; // Delays to push/v1 (seconds) class HttpMInvalCache; // Used for caches to keep track of invalidations class InvalidationRec { public: InvalidationRec(const char *pid, double mtime, char updating = 0) { pg_ = new char[strlen(pid) + 1]; strcpy(pg_, pid); mtime_ = mtime; scount_ = HTTP_HBEXPIRE_COUNT; updating_ = updating; next_ = 0, prev_ = 0; } virtual ~InvalidationRec() { delete []pg_; } const char* pg() const { return pg_; } double mtime() const { return mtime_; } char updating() const { return updating_; } int scount() const { return scount_; } InvalidationRec* next() { return next_; } void reset(double mtime) { scount_ = HTTP_HBEXPIRE_COUNT; mtime_ = mtime; } int dec_scount() { return --scount_; } void set_updating() { updating_ = 1; } void clear_updating() { updating_ = 0; } void insert(InvalidationRec **head) { next_ = *head; if (next_ != 0) next_->prev_ = &next_; prev_ = head; *head = this; } void detach() { if (prev_ != 0) *prev_ = next_; if (next_ != 0) next_->prev_ = prev_; } friend class HttpMInvalCache; protected: char *pg_; double mtime_; char updating_; // 1 if an update is going to be sent soon int scount_; // Times that an invalidation needs to be multicast InvalidationRec *next_; InvalidationRec **prev_; }; class HBTimer : public TimerHandler { public: HBTimer(HttpMInvalCache *a, double interval) : TimerHandler() { a_ = a, interval_ = interval; } void set_interval(double interval) { interval_ = interval; } double get_interval() const { return interval_; } double next_interval() { return interval_ * (1 + Random::uniform(-0.1,0.1)); } void sched() { TimerHandler::sched(interval_); } void resched() { TimerHandler::resched(next_interval()); } protected: virtual void expire(Event *e); virtual void handle(Event *e) { TimerHandler::handle(e); resched(); } HttpMInvalCache *a_; double interval_; }; class PushTimer : public TimerHandler { public: PushTimer(HttpMInvalCache *a, double itv) : TimerHandler(), a_(a), interval_(itv) {} void set_interval(double itv) { interval_ = itv; } double get_interval() const { return interval_; } void sched() { TimerHandler::sched(interval_); } protected: virtual void expire(Event *e); HttpMInvalCache *a_; double interval_; }; class LivenessTimer : public TimerHandler { public: LivenessTimer(HttpMInvalCache *a, double itv, int nbr) : TimerHandler(), a_(a), nbr_(nbr), interval_(itv) {} void sched() { TimerHandler::sched(interval_); } void resched() { TimerHandler::resched(interval_); } protected: virtual void expire(Event *e); HttpMInvalCache *a_; // The cache to be alerted int nbr_; // Neighbor cache id double interval_; }; // XXX ADU defined in app-connector.h const int HTTPDATA_COST = 8; // User-level packets class HttpData : public AppData { private: int id_; // ID of the sender public: HttpData() : AppData(HTTP_DATA) {} HttpData(AppDataType t, int d) : AppData(t) { id_ = d; } HttpData(HttpData& d) : AppData(d) { id_ = d.id_; } inline int& id() { return id_; } virtual int size() const { return sizeof(HttpData); } virtual int cost() const { return HTTPDATA_COST; } virtual AppData* copy() { return (new HttpData(*this)); } }; // HTTP data during normal request and response: containing a tcl command class HttpNormalData : public HttpData { private: char *str_; int strlen_; int cost_; public: // XXX Whoever sends data should allocate memory to store the string. // Whoever receives this object should delete it to free the string. HttpNormalData(int id, int cost, const char *str) : HttpData(HTTP_NORMAL, id) { if ((str == NULL) || (*str == 0)) strlen_ = 0; else strlen_ = strlen(str) + 1; if (strlen_ > 0) { str_ = new char[strlen_]; strcpy(str_, str); } else str_ = NULL; cost_ = cost; } HttpNormalData(HttpNormalData& d) : HttpData(d) { cost_ = d.cost_; strlen_ = d.strlen_; if (strlen_ > 0) { str_ = new char[strlen_]; strcpy(str_, d.str_); } else str_ = NULL; } virtual ~HttpNormalData() { if (str_ != NULL) delete []str_; } virtual int size() const { // Intentially use sizeof(int) instead of 2*sizeof(int) return (sizeof(HttpData)+sizeof(int)+strlen_); } virtual int cost() const { return cost_; } char* str() const { return str_; } virtual AppData* copy() { return (new HttpNormalData(*this)); } }; // XXX assign cost to a constant so as to be more portable const int HTTPHBDATA_COST = 32; const int HTTP_MAXURLLEN = 20; // Struct used to pack invalidation records into packets class HttpHbData : public HttpData { protected: struct InvalRec { char pg_[HTTP_MAXURLLEN]; // Maximum page id length double mtime_; // Used only to mark that this page will be send in the // next multicast update. The updating field in this agent // will only be set after it gets the real update. int updating_; void copy(InvalidationRec *v) { strcpy(pg_, v->pg()); mtime_ = v->mtime(); updating_ = v->updating(); } InvalidationRec* copyto() { return (new InvalidationRec(pg_, mtime_, updating_)); } }; private: int num_inv_; InvalRec* inv_rec_; inline InvalRec* inv_rec() { return inv_rec_; } public: HttpHbData(int id, int n) : HttpData(HTTP_INVALIDATION, id), num_inv_(n) { if (num_inv_ > 0) inv_rec_ = new InvalRec[num_inv_]; else inv_rec_ = NULL; } HttpHbData(HttpHbData& d) : HttpData(d) { num_inv_ = d.num_inv_; if (num_inv_ > 0) { inv_rec_ = new InvalRec[num_inv_]; memcpy(inv_rec_, d.inv_rec_,num_inv_*sizeof(InvalRec)); } else inv_rec_ = NULL; } virtual ~HttpHbData() { if (inv_rec_ != NULL) delete []inv_rec_; } virtual int size() const { return HttpData::size() + num_inv_*sizeof(InvalRec); } // XXX byte cost to appear in trace file virtual int cost() const { // Minimum size 1 so that TCP will send a packet return (num_inv_ == 0) ? 1 : (num_inv_*HTTPHBDATA_COST); } virtual AppData* copy() { return (new HttpHbData(*this)); } inline int& num_inv() { return num_inv_; } inline void add(int i, InvalidationRec *r) { inv_rec()[i].copy(r); } inline char* rec_pg(int i) { return inv_rec()[i].pg_; } inline double& rec_mtime(int i) { return inv_rec()[i].mtime_; } void extract(InvalidationRec*& ivlist); }; class HttpUpdateData : public HttpData { protected: // Pack page updates to be pushed to caches struct PageRec { char pg_[HTTP_MAXURLLEN]; double mtime_; double age_; int size_; void copy(ClientPage *p) { p->name(pg_); mtime_ = p->mtime(); age_ = p->age(); size_ = p->size(); } }; private: int num_; int pgsize_; PageRec *rec_; inline PageRec* rec() { return rec_; } public: HttpUpdateData(int id, int n) : HttpData(HTTP_UPDATE, id) { num_ = n; pgsize_ = 0; if (num_ > 0) rec_ = new PageRec[num_]; else rec_ = NULL; } HttpUpdateData(HttpUpdateData& d) : HttpData(d) { num_ = d.num_; pgsize_ = d.pgsize_; if (num_ > 0) { rec_ = new PageRec[num_]; memcpy(rec_, d.rec_, num_*sizeof(PageRec)); } else rec_ = NULL; } virtual ~HttpUpdateData() { if (rec_ != NULL) delete []rec_; } virtual int size() const { return HttpData::size() + 2*sizeof(int)+num_*sizeof(PageRec); } virtual int cost() const { return pgsize_; } virtual AppData* copy() { return (new HttpUpdateData(*this)); } inline int num() const { return num_; } inline int pgsize() const { return pgsize_; } inline void set_pgsize(int s) { pgsize_ = s; } inline void add(int i, ClientPage *p) { rec()[i].copy(p); pgsize_ += p->size(); } inline char* rec_page(int i) { return rec()[i].pg_; } inline int& rec_size(int i) { return rec()[i].size_; } inline double& rec_age(int i) { return rec()[i].age_; } inline double& rec_mtime(int i) { return rec()[i].mtime_; } }; const int HTTPLEAVE_COST = 4; // Message: server leave class HttpLeaveData : public HttpData { private: int num_; int* rec_; // array of server ids which are out of contact inline int* rec() { return rec_; } public: HttpLeaveData(int id, int n) : HttpData(HTTP_LEAVE, id) { num_ = n; if (num_ > 0) rec_ = new int[num_]; else rec_ = NULL; } HttpLeaveData(HttpLeaveData& d) : HttpData(d) { num_ = d.num_; if (num_ > 0) { rec_ = new int[num_]; memcpy(rec_, d.rec_, num_*sizeof(int)); } else rec_ = NULL; } virtual ~HttpLeaveData() { if (rec_ != NULL) delete []rec_; } virtual int size() const { return HttpData::size() + (num_+1)*sizeof(int); } virtual int cost() const { return num_*HTTPLEAVE_COST; } virtual AppData* copy() { return (new HttpLeaveData(*this)); } inline int num() const { return num_; } inline void add(int i, int id) { rec()[i] = id; } inline int rec_id(int i) { return rec()[i]; } }; // Auxliary class class NeighborCache { public: NeighborCache(HttpMInvalCache*c, double t, LivenessTimer *timer) : cache_(c), time_(t), down_(0), timer_(timer) {} ~NeighborCache(); double time() { return time_; } void reset_timer(double time) { time_ = time, timer_->resched(); } int is_down() { return down_; } void down() { down_ = 1; } void up() { down_ = 0; } int num() const { return sl_.num(); } HttpMInvalCache* cache() { return cache_; } void pack_leave(HttpLeaveData&); int is_server_down(int sid); void server_down(int sid); void server_up(int sid); void invalidate(HttpMInvalCache *c); void add_server(int sid); // Maintaining neighbor cache timeout entries struct ServerEntry { ServerEntry(int sid) : server_(sid), next_(NULL), down_(0) {} int server() { return server_; } ServerEntry* next() { return next_; } int is_down() { return down_; } void down() { down_ = 1; } void up() { down_ = 0; } int server_; ServerEntry *next_; int down_; }; // We cannot use template. :((( struct ServerList { ServerList() : head_(NULL), num_(0) {} void insert(ServerEntry *s) { s->next_ = head_; head_ = s; num_++; } // We don't need a detach() ServerEntry* gethead() { return head_; } // For iterations int num() const { return num_; } ServerEntry *head_; int num_; }; protected: HttpMInvalCache* cache_; double time_; int down_; ServerList sl_; LivenessTimer *timer_; }; #endif // ns_http_aux_h