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
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
00107 clientStub.Start();
00108 backgroundFetcher.Start();
00109 customProtocolStack.Start();
00110
00111 processingThread.Start();
00112 msgLog.LogStartComplete("GPRSWeb Server Processing");
00113 }
00114
00115 public void Stop() {
00116 msgLog.LogStopBegin("GPRSWeb Server processing");
00117
00118 clientStub.Stop();
00119 backgroundFetcher.Stop();
00120
00121 processingThread.Abort();
00122 }
00123
00124 void run() {
00125 HTTPRequestQueueObject reqQObj;
00126 HTTPResponse response;
00127 try {
00128 while (true) {
00129
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
00147 if (cacheManager.CanService(request) && !request.HasPragmaNoCacheSet) {
00148
00149
00150 response = cacheManager.ServiceRequest(request);
00151 } else {
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
00157 if (response != null && response.StatusCode != 304) {
00158 cacheManager.UpdateCache(response, request.URI);
00159 }
00160 }
00161
00162
00163 if (response != null && response.ContentType != null) {
00164 if (response.ContentType.StartsWith("image")) {
00165 HTTPResponse cachedResponse = response;
00166 try {
00167 response = DegradeImage(response);
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);
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;
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
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) {
00268 lock (chkLists.SyncRoot) {
00269 CacheIndex clientChkList = chkLists[reqQObj.sourceDeviceID];
00270 bool indexRequestSent = false;
00271 while (clientChkList == null) {
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) {
00283 byte[] respCHK = response.GetCHK();
00284
00285 if (clientChkList.Contains(respCHK)) {
00286 if (isCacheUpdate) {
00287 msg = new CacheUpdateNoChangeMsg(reqQObj.URI, respCHK, response.Headers);
00288 } else {
00289 msg = new NoChangeMessage(respCHK, reqQObj.callbackReceipt, response.Headers);
00290 }
00291 UpdateChkListEntries(ref clientChkList, respCHK, response.Body);
00292 msgLog.LogSend("\"No Change\" message sent for {0}", reqQObj.URI);
00293 } else {
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 UpdateChkListEntries(ref clientChkList, respCHK, responseBody);
00304 msgLog.LogSend("\"HTTP Response\" message sent for {0}", reqQObj.URI);
00305 }
00306 } else {
00307
00308
00309
00310
00311
00312 EncodedHTTPResponse encodedResponse = new EncodedHTTPResponse(response);
00313 if (!isCacheUpdate) {
00314 msg = new HTTPResponseMessage(encodedResponse, reqQObj.callbackReceipt);
00315 } else {
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 }
00321 } else {
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 {
00339
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 }