Main Page | Class Hierarchy | Class List | File List | Class Members

MCP.cs

00001 using System;
00002 using System.Collections;
00003 using System.Collections.Specialized;
00004 using System.Threading;
00005 using System.IO;
00006 using System.Text;
00007 using System.Text.RegularExpressions;
00008 using System.Drawing;
00009 using System.Drawing.Imaging;
00010 using Common;
00011 using ProtocolStack;
00012 
00013 namespace Server {
00017         public class MCP {
00019                 // TODO: Remove this hack
00020                 const ushort HACK_CLIENT_ID = 1;
00022 
00023 
00024 
00025                 public HTTPObjectQueue requestQueue;
00029                 public ClientStub clientStub;
00033                 StackInterface customProtocolStack;
00037                 public ServerCacheManager cacheManager;
00038 
00042                 public HTTPStub httpStub; 
00043 
00047                 OutgoingMessageQueue messageOutputQ;
00048                 
00052                 Thread processingThread;
00053 
00057                 ClientChkLists chkLists;
00058 
00062                 BackgroundFetcher backgroundFetcher;
00063 
00067                 internal MessageLogger msgLog;
00068 
00072                 internal ServerSettings settings;
00073 
00075 
00076 
00077 
00078 
00079 
00080                 public MCP() {
00081                         msgLog = new MessageLogger(Console.Out);
00082                         settings = new ServerSettings();
00083                         msgLog.LogStartBegin("GPRSWeb Server Library (version {0}, build {1})", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(2), System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Build);
00084                         customProtocolStack = new StackInterface(new ConnectionResetDelegate(ResetConnection));
00085                         requestQueue = new HTTPObjectQueue();
00086                         clientStub = new ClientStub(HACK_CLIENT_ID, customProtocolStack.ReceivedMessages, requestQueue, this);
00087                         messageOutputQ = customProtocolStack.MessagesToSend;
00088                         cacheManager = new ServerCacheManager(this);
00089                         backgroundFetcher = new BackgroundFetcher(this);
00090                         httpStub = new HTTPStub();
00091                         chkLists = new ClientChkLists();
00092                         processingThread = new Thread(new ThreadStart(run));
00093                         msgLog.LogStartComplete("GPRSWeb Server Library");
00094                 }
00095 
00099                 public void ResetConnection() {
00100                         msgLog.LogError("CONNECTION RESET");
00101                         customProtocolStack.ResetConnection();
00102                 }
00103 
00104                 public void Start() {
00105                         msgLog.LogStartBegin("GPRSWeb Server Processing");
00106                         /* start other threads in correct order */
00107                         clientStub.Start();
00108                         backgroundFetcher.Start();
00109                         customProtocolStack.Start();
00110                         /* start this thead */
00111                         processingThread.Start();
00112                         msgLog.LogStartComplete("GPRSWeb Server Processing");
00113                 }
00114 
00115                 public void Stop() {
00116                         msgLog.LogStopBegin("GPRSWeb Server processing");
00117                         /* abort threads in correct order */
00118                         clientStub.Stop();
00119                         backgroundFetcher.Stop();
00120                         /* abort this thread */
00121                         processingThread.Abort();
00122                 }
00123 
00124                 void run() {
00125                         HTTPRequestQueueObject reqQObj;
00126                         HTTPResponse response;
00127                         try {
00128                                 while (true) {
00129                                         // get request from Request Queue
00130                                         reqQObj = (HTTPRequestQueueObject)requestQueue.DequeueBlocking();
00131                                         response = ServiceHTTPRequest(reqQObj);
00132                                         SendResponse(response, reqQObj, false);
00133                                 }
00134                         } catch (ThreadAbortException) {
00135                                 Console.WriteLine("(Server MCP: Shutting Down Request Processing Thread)");
00136                         }
00137                 }
00138 
00144                 public HTTPResponse ServiceHTTPRequest(HTTPRequestQueueObject request) {
00145                         HTTPResponse response; 
00146                         // obtain response, from cache or from server
00147                         if (cacheManager.CanService(request) && !request.HasPragmaNoCacheSet) {/*DISABLE CACHE*/
00148                                 // cache update optional, depending on if we freshened /*DISABLE CACHE*/
00149                                 // update occurs in-cache. /*DISABLE CACHE*/
00150                                 response = cacheManager.ServiceRequest(request); /*DISABLE CACHE*/
00151                         } else { /*DISABLE CACHE*/
00152                                 if (request.HasPragmaNoCacheSet) {
00153                                         Console.WriteLine("- Pragma: no-cache is set in request for {0}", request.URI);
00154                                 }
00155                                 response = httpStub.ServiceRequest(request);
00156                                 // always update cache when we've fetched new stuff
00157                                 if (response != null && response.StatusCode != 304) {
00158                                         cacheManager.UpdateCache(response, request.URI); /*DISABLE CACHE*/
00159                                 }
00160                         } /*DISABLE CACHE*/
00161 
00162                         // hooks for extra processing based on content type
00163                         if (response != null && response.ContentType != null) {
00164                                 if (response.ContentType.StartsWith("image")) {
00165                                         HTTPResponse cachedResponse = response;
00166                                         try {
00167                                                 response = DegradeImage(response); /*DISABLE DEGRADE*/
00168                                         } catch (Exception ex) {
00169                                                 Console.Error.WriteLine("*** Unable to degrade image. Using original ***\n\n{0}", ex);
00170                                                 response = cachedResponse;
00171                                         }
00172                                 } else if (response.ContentType.StartsWith("text/html") && (response.Body != null)) {
00173                                         BeginParseAndPush(request, response);/*DISABLE PARSE n PUSH*/
00174                                 }
00175                         }
00176                         return response;
00177                 }
00178 
00184                 private HTTPResponse DegradeImage(HTTPResponse response) {
00185                         if (!response.ContentType.StartsWith("image/jpeg")) {
00186                                 Console.WriteLine("Only degrading of JPEG images is currently supported by Windows GDI+");
00187                         } else {
00188                                 Console.WriteLine("Recompressing JPEG image to 10% using Windows GDI+");
00189 
00190                                 MemoryStream imgStream = new MemoryStream(response.Body.data);
00191                                 Bitmap bitmap = new Bitmap(imgStream);
00192                                 MemoryStream degradedImageStream = new MemoryStream();
00193                                 ImageCodecInfo[] codecInfo = ImageCodecInfo.GetImageEncoders();
00194                                 ImageCodecInfo encoderCodecInfo = null;
00195                                 foreach(ImageCodecInfo icInfo in codecInfo) {
00196                                         if (icInfo.MimeType.Equals(response.ContentType)) {
00197                                                 encoderCodecInfo = icInfo;
00198                                                 break;
00199                                         }
00200                                 }
00201                                 System.Drawing.Imaging.Encoder qualityEncoder = System.Drawing.Imaging.Encoder.Quality;
00202                                 EncoderParameters myEncoderParameters = new EncoderParameters(1);
00203                                 myEncoderParameters.Param[0] = new EncoderParameter(qualityEncoder, 10L);
00204                                 bitmap.Save(degradedImageStream, encoderCodecInfo, myEncoderParameters);
00205                                 response.Body.data = new byte[degradedImageStream.Length];
00206                                 degradedImageStream.Seek(0, SeekOrigin.Begin);
00207                                 degradedImageStream.Read(response.Body.data, 0, response.Body.data.Length);
00208                                 response.ContentLength = response.Body.data.Length.ToString();
00209                         }
00210                         return response;
00211                 }
00212 
00213 
00214 
00219                 private void BeginParseAndPush(HTTPRequestQueueObject originalRequest, HTTPResponse htmlResponse) {
00220                         HTTPRequest[] httpRequests = ImageUriParser.GenerateImageRequests(new MessageLogger(Console.Out), originalRequest, htmlResponse);
00221                         HTTPRequestQueueObject[] imageRequests = new HTTPRequestQueueObject[httpRequests.Length];
00222                         for (int i = 0; i < httpRequests.Length; i++) {
00223                                 if (httpRequests[i] != null) {
00224                                         imageRequests[i] = new HTTPRequestQueueObject(httpRequests[i]);
00225                                         imageRequests[i].sourceDeviceID = originalRequest.sourceDeviceID;
00226                                 } else {
00227                                         imageRequests[i] = null; // explictyly set image request to null if we generated a null http request earlier. Will be skipped over later, and probably optimised away.
00228                                 }
00229                         }
00230                         Console.WriteLine("- Parse phase generated {0} new requests", imageRequests.Length);
00231                         foreach (HTTPRequestQueueObject rqo in imageRequests) {
00232                                 if (rqo != null) {
00233                                         backgroundFetcher.AddToFetchQueue(rqo); 
00234                                 }
00235                         }
00236                 }
00237 
00238 
00243                 private void SendMessage(Message msg, bool isCacheUpdate) {
00244                         // selecting output queue is done in protocol stack
00245                         msg.isLowPriority = isCacheUpdate;
00246                         messageOutputQ.EnqueueBlocking(msg);
00247 
00248                 }
00249 
00255                 public void UpdateChkList(ushort clientID, CacheIndex index) {
00256                         lock (chkLists.SyncRoot) {
00257                                 chkLists.Add(clientID, index);
00258                                 msgLog.LogReceive("CACHE INDEX received");
00259                                 Monitor.PulseAll(chkLists.SyncRoot);
00260                         }
00261                 }
00262 
00263                 public void SendResponse(HTTPResponse response, HTTPRequestQueueObject reqQObj, bool isCacheUpdate) {
00264                         bool isMessageToSend = true;
00265                         Message msg = null;
00266                         try {
00267                                 if (response != null)  { // so long as it didn't screw up...
00268                                         lock (chkLists.SyncRoot) {
00269                                                 CacheIndex clientChkList = chkLists[reqQObj.sourceDeviceID];
00270                                                 bool indexRequestSent = false;
00271                                                 while (clientChkList == null) { // wait for a client check list to get here. Assumes only one client, as far as I can tell
00272                                                         if (!indexRequestSent) {
00273                                                                 msg = new CacheIndexRequestMessage(reqQObj.sourceDeviceID);
00274                                                                 SendMessage(msg, false);
00275                                                                 indexRequestSent = true;
00276                                                                 msgLog.Log("Cache Index Requested. Sleeping now");
00277                                                         }
00278                                                         Monitor.Wait(chkLists.SyncRoot);
00279                                                         clientChkList = chkLists[reqQObj.sourceDeviceID];
00280                                                 }
00281                                                 Monitor.PulseAll(chkLists.SyncRoot);
00282                                                 if (response.Body != null) { // response has a body
00283                                                         byte[] respCHK = response.GetCHK();
00284                                                         // check client CHK list for contents. 
00285 /*Comment to DISABLECACHE*/     if (clientChkList.Contains(respCHK)) { // if we think client cache already has this...
00286 /*Comment to DISABLECACHE*/             if (isCacheUpdate) { 
00287 /*Comment to DISABLECACHE*/                     msg = new CacheUpdateNoChangeMsg(reqQObj.URI, respCHK, response.Headers);
00288 /*Comment to DISABLECACHE*/             } else {
00289 /*Comment to DISABLECACHE*/                     msg = new NoChangeMessage(respCHK, reqQObj.callbackReceipt, response.Headers);
00290 /*Comment to DISABLECACHE*/             }
00291 /*Comment to DISABLECACHE*/             UpdateChkListEntries(ref clientChkList, respCHK, response.Body);
00292 /*Comment to DISABLECACHE*/             msgLog.LogSend("\"No Change\" message sent for {0}", reqQObj.URI);
00293                                                         } else { /*DISABLE CACHE*/
00294                                                                 HTTPBody responseBody = new HTTPBody(response.Body.data);
00295                                                                 EncodedHTTPResponse encodedResponse = new EncodedHTTPResponse(response, reqQObj.originalChk, cacheManager);
00296                                                                 if (isCacheUpdate) {
00297                                                                         CacheUpdateHTTPResponseMsg cacheUpdateMsg = new CacheUpdateHTTPResponseMsg(reqQObj.URI, encodedResponse);
00298                                                                         msg = cacheUpdateMsg;
00299                                                                 } else {
00300                                                                         HTTPResponseMessage responseMsg = new HTTPResponseMessage(encodedResponse, reqQObj.callbackReceipt);
00301                                                                         msg = responseMsg;
00302                                                                 }
00303 /*Comment to DISABLECACHE*/             UpdateChkListEntries(ref clientChkList, respCHK, responseBody);
00304                                                                 msgLog.LogSend("\"HTTP Response\" message sent for {0}", reqQObj.URI);
00305 /*Comment to DISABLECACHE*/     }
00306                                                 } else { // response with no body
00307                                                         /* 
00308                                                          * We have a change here from 1.0 - previously we didn't send a cache update if there was no body. 
00309                                                          * In order for the pending requests on the client to function correctly, we need to send pretty much
00310                                                          * everything
00311                                                          */
00312                                                         EncodedHTTPResponse encodedResponse = new EncodedHTTPResponse(response);
00313                                                         if (!isCacheUpdate) { // not cache update, so do regular response with no body. Hope it doesn't crash with null ref!
00314                                                                 msg = new HTTPResponseMessage(encodedResponse, reqQObj.callbackReceipt);
00315                                                         } else { // is cache update, do cache update response with no body.
00316                                                                 msg = new CacheUpdateHTTPResponseMsg(reqQObj.URI, encodedResponse);
00317                                                         }
00318                                                         msgLog.LogSend("{0} message (no body) to service {1}", msg.GetType().Name, reqQObj.URI);
00319                                                 }
00320                                         } // end lock on chkList
00321                                 } else { // response == null
00322                                         isMessageToSend = false;
00323                                 }
00324                         } catch (CacheNotFoundException ex) {
00325                                 Console.WriteLine("*** Cache Entry not found: {0} ***", ex.ToString());
00326                                 isMessageToSend = false;
00327                         }
00328                         if (isMessageToSend) {
00329                                 msg.Destination = reqQObj.sourceDeviceID;
00330                                 SendMessage(msg, isCacheUpdate);
00331                         }
00332                 }
00333 
00334                 public void UpdateChkListEntries(ref CacheIndex index, byte[] chk, HTTPBody body) {
00335                         if (index.Contains(chk)) {
00336                                 ((CacheIndexEntry)index[chk]).lastUsed = DateTime.Now;
00337                                 Console.WriteLine("- Updated CHK list entry to current time");
00338                         } else { // we have to add
00339                                 // check size and see if we have to purge
00340                                 bool flag = true;
00341                                 int size = CacheEntry.SizeOfSerialised(body);
00342                                 while (index.GetSpaceRemaining() < size) {
00343                                         if (flag) { Console.WriteLine("- Purging CHK list"); flag = false; }
00344                                         index.Remove(index.LeastRecentlyUsed().chk);
00345                                 }
00346                                 Console.WriteLine("- Added new CHK List Entry");
00347                                 index.Add(new CacheIndexEntry(chk, Cache.StringFromChk(chk), size, DateTime.Now)); 
00348                         }
00349                 }
00350 
00351         }
00352 
00353         
00354  
00355         public class ClientChkLists : ListDictionary {
00356                 class MyComparer: IComparer {
00357                         public int Compare(object x, object y) {
00358                                 ushort a = (ushort)x;
00359                                 ushort b = (ushort)y;
00360                                 return a-b;
00361                         }
00362                 }
00363 
00364                 public ClientChkLists() : base(new MyComparer()) {}
00365 
00366                 public void Add(ushort deviceID, CacheIndex chkList) {
00367                         base[deviceID] = chkList;
00368                 }
00369 
00370                 public CacheIndex this[ushort index] {
00371                         get { return (CacheIndex)base[index]; }
00372                         set { base[index] = value; }
00373                 }
00374         }
00375 }

Generated on Mon May 8 22:07:27 2006 by  doxygen 1.3.9.1