зеркало из https://github.com/mozilla/pjs.git
2323 строки
62 KiB
C
2323 строки
62 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/* Module to do memory caching; storage and retreival
|
|
* very simple hash table based scheme.
|
|
*
|
|
* designed and originally implemented by Lou Montulli
|
|
* modifications/additions by Gagan Saksena
|
|
*/
|
|
|
|
/* Please leave outside of ifdef for windows precompiled headers */
|
|
#include "rosetta.h"
|
|
#include "xp.h"
|
|
|
|
#ifdef MOZILLA_CLIENT
|
|
|
|
#include "mkcache.h"
|
|
#include "mkselect.h"
|
|
#include "netutils.h"
|
|
#include "mktcp.h"
|
|
#include "glhist.h"
|
|
#include "xp_hash.h"
|
|
#include "xp_mcom.h"
|
|
#include "client.h"
|
|
#include "mkgeturl.h"
|
|
#include "mkstream.h"
|
|
#include "extcache.h"
|
|
#include "mkmemcac.h"
|
|
#ifndef MODULAR_NETLIB
|
|
#include "libimg.h" /* Image Lib public API. */
|
|
#else
|
|
extern void IL_SetCacheSize(uint32 new_size);
|
|
#endif
|
|
#include "prclist.h"
|
|
#include "prmem.h"
|
|
#include "plstr.h"
|
|
#include "shist.h"
|
|
#include "secnav.h"
|
|
#include "timing.h"
|
|
|
|
#ifdef NU_CACHE
|
|
#include "CacheStubs.h"
|
|
#endif
|
|
|
|
/* exported error ints */
|
|
extern int MK_OUT_OF_MEMORY;
|
|
|
|
/* the size of the cache segment output size
|
|
*/
|
|
#define NET_MEM_CACHE_OUTPUT_SIZE 2048
|
|
|
|
/* MAX_MEMORY_ALLOC_SIZE is the size of the largest malloc
|
|
* used in this module
|
|
*
|
|
* the NET_Socket_Buffer can never be larger than the
|
|
* MAX_MEMORY_ALLOC_SIZE or this will fail
|
|
*/
|
|
#ifdef XP_WIN16
|
|
#define MAX_MEMORY_ALLOC_SIZE 32767 /* MAXINT */
|
|
#elif defined(XP_MAC)
|
|
#define MAX_MEMORY_ALLOC_SIZE 64*1024
|
|
#else
|
|
#define MAX_MEMORY_ALLOC_SIZE (((unsigned) (~0) << 1) >> 1) /* MAXINT */
|
|
|
|
/* @@@@@@@@@ there is currently a limit on PR_Malloc to less than 32 K */
|
|
#undef MAX_MEMORY_ALLOC_SIZE
|
|
#define MAX_MEMORY_ALLOC_SIZE 32767 /* MAXINT */
|
|
#endif
|
|
|
|
/* this is the minimum size of each of the memory segments used to hold
|
|
* the cache object in memory
|
|
*/
|
|
#ifdef XP_MAC
|
|
#define MEMORY_CACHE_SEGMENT_SIZE 64*1024
|
|
#else
|
|
#define MEMORY_CACHE_SEGMENT_SIZE 2048
|
|
#endif
|
|
|
|
/* !!! structure is typedef'd to net_MemoryCacheObject
|
|
* in net.h
|
|
*/
|
|
struct _net_MemoryCacheObject {
|
|
XP_List *list;
|
|
net_CacheObject cache_obj;
|
|
int external_locks; /* locks set by other modules calling in */
|
|
int mem_read_lock; /* the number of current readers */
|
|
XP_Bool delete_me; /* set this to delete the object
|
|
* once all readers are done
|
|
*/
|
|
XP_Bool current_page_only; /* this is set when the document
|
|
* requested not to be cached.
|
|
* When set the document should
|
|
* only be served if it's a
|
|
* resize or print
|
|
*/
|
|
XP_Bool completed; /* whether or not we have completed the
|
|
* caching of this object. Initialized to
|
|
* false, then set to true once the
|
|
* complete function is called.
|
|
*/
|
|
XP_Bool aborted; /* whether or not we have aborted the
|
|
* caching of this object.
|
|
*/
|
|
};
|
|
|
|
/* structure to hold memory
|
|
* segments
|
|
*/
|
|
typedef struct _net_MemorySegment {
|
|
char *segment;
|
|
uint32 seg_size;
|
|
uint32 in_use;
|
|
} net_MemorySegment;
|
|
|
|
/* the hash table pointer that holds all the memory cache
|
|
* objects for quick lookup
|
|
*/
|
|
PRIVATE XP_HashList * net_MemoryCacheHashList = 0;
|
|
|
|
/* semaphore counter set when calling any of the list add functions
|
|
*/
|
|
PRIVATE int net_cache_adding_object=0;
|
|
|
|
/* a list of all the documents currently in
|
|
* memory
|
|
*
|
|
* The net_MemoryCacheList is used as a delete queue
|
|
* and is ordered by last-accessed time.
|
|
*/
|
|
PRIVATE XP_List *net_MemoryCacheList=0;
|
|
PRIVATE uint32 net_MemoryCacheSize=0;
|
|
PRIVATE uint32 net_MaxMemoryCacheSize=0;
|
|
|
|
/* this object is used by the MemCacheConverter.
|
|
* MemCacheConverter is a standard netlib stream and uses
|
|
* this structure to hold data between invokations of
|
|
* the write function and complete or abort.
|
|
*/
|
|
typedef struct _CacheDataObject {
|
|
PRCList links;
|
|
NET_StreamClass *next_stream;
|
|
net_MemoryCacheObject *memory_copy;
|
|
URL_Struct *URL_s;
|
|
uint32 computed_content_length;
|
|
} CacheDataObject;
|
|
|
|
/* PRIVATE XXX Mac CodeWarrior bug */ PRCList mem_active_cache_data_objects
|
|
= PR_INIT_STATIC_CLIST(&mem_active_cache_data_objects);
|
|
|
|
/* free a segmented memory copy of an object
|
|
* and the included net_cacheObject struct
|
|
*/
|
|
PRIVATE void
|
|
net_FreeMemoryCopy(net_MemoryCacheObject * mem_copy)
|
|
{
|
|
net_MemorySegment * mem_seg;
|
|
|
|
PR_ASSERT(mem_copy);
|
|
|
|
if(!mem_copy)
|
|
return;
|
|
|
|
/* If the stream was aborted while being written, and nobody is
|
|
* reading it, then it's ok to delete it now. That is,
|
|
* we don't need to check if we want to set delete_me
|
|
*/
|
|
if (!((mem_copy->mem_read_lock == 0) && mem_copy->aborted))
|
|
{
|
|
/* if this object is currently being read or written DO NOT delete it.
|
|
* It will be deleted by another call to this function
|
|
* after it is finished being read.
|
|
*/
|
|
if (((mem_copy->mem_read_lock > 0) && mem_copy->completed) ||
|
|
!mem_copy->completed)
|
|
{
|
|
TRACEMSG(("Seting delete_me flag on memory cache object"));
|
|
mem_copy->delete_me = TRUE;
|
|
|
|
/* remove it from the hash list so it won't be found
|
|
* by NET_GetURLInMemCache
|
|
*/
|
|
XP_HashListRemoveObject(net_MemoryCacheHashList, mem_copy);
|
|
return;
|
|
}
|
|
}
|
|
|
|
TRACEMSG(("Freeing memory cache copy"));
|
|
|
|
/* free and delete the memory segment list
|
|
*/
|
|
while((mem_seg = (net_MemorySegment *)XP_ListRemoveTopObject(mem_copy->list)) != NULL)
|
|
{
|
|
/* reduce the global memory cache size
|
|
*/
|
|
net_MemoryCacheSize -= mem_seg->seg_size;
|
|
PR_Free(mem_seg->segment);
|
|
PR_Free(mem_seg);
|
|
}
|
|
|
|
XP_ListDestroy(mem_copy->list);
|
|
|
|
/* Remove it from the hash and delete lists,
|
|
* If the object isn't in these lists anymore
|
|
* the call will be ignored.
|
|
*/
|
|
XP_HashListRemoveObject(net_MemoryCacheHashList, mem_copy);
|
|
XP_ListRemoveObject(net_MemoryCacheList, mem_copy);
|
|
|
|
PR_FREEIF(mem_copy->cache_obj.address);
|
|
PR_FREEIF(mem_copy->cache_obj.post_data);
|
|
PR_FREEIF(mem_copy->cache_obj.post_headers);
|
|
PR_FREEIF(mem_copy->cache_obj.content_type);
|
|
PR_FREEIF(mem_copy->cache_obj.charset);
|
|
PR_FREEIF(mem_copy->cache_obj.content_encoding);
|
|
|
|
PR_Free(mem_copy);
|
|
|
|
}
|
|
|
|
/* removes the last mem object. Returns negative on error,
|
|
* otherwise returns the current size of the cache
|
|
*/
|
|
PRIVATE int32
|
|
net_remove_last_memory_cache_object(void)
|
|
{
|
|
net_MemoryCacheObject * mem_cache_obj;
|
|
|
|
/* safty valve in case there are no list items
|
|
* or if we are in the process of adding an object
|
|
*/
|
|
if(net_cache_adding_object)
|
|
return -1;
|
|
|
|
mem_cache_obj = (net_MemoryCacheObject *)
|
|
XP_ListRemoveEndObject(net_MemoryCacheList);
|
|
|
|
if(!mem_cache_obj)
|
|
return -1;
|
|
|
|
/* we can't remove it if we have external locks or if it's not completed -
|
|
* try removing another one
|
|
*/
|
|
if(mem_cache_obj->external_locks || !mem_cache_obj->completed)
|
|
{
|
|
int status = net_remove_last_memory_cache_object(); /* recurse */
|
|
/* add the object back in */
|
|
XP_ListAddObject(net_MemoryCacheList, mem_cache_obj);
|
|
return status;
|
|
}
|
|
|
|
net_FreeMemoryCopy(mem_cache_obj);
|
|
|
|
return((int32) net_MemoryCacheSize);
|
|
}
|
|
|
|
/* this function free's objects to bring the cache size
|
|
* below the size passed in
|
|
*/
|
|
PRIVATE void
|
|
net_ReduceMemoryCacheTo(uint32 size)
|
|
{
|
|
/* safty valve in case we are in the process of adding an object
|
|
*/
|
|
if(net_cache_adding_object)
|
|
return;
|
|
|
|
while(net_MemoryCacheSize > size)
|
|
{
|
|
if(0 > net_remove_last_memory_cache_object())
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set the size of the Memory cache.
|
|
* Set it to 0 if you want cacheing turned off
|
|
* completely
|
|
*/
|
|
#define HTML_CACHE_MAX_BASE_SIZE 1024l*1024l
|
|
#define HTML_CACHE_VARIABLE_GROWTH .05 /* the percentage to add */
|
|
PUBLIC void
|
|
NET_SetMemoryCacheSize(int32 new_size)
|
|
{
|
|
int32 image_cache_size;
|
|
int32 html_cache_size;
|
|
|
|
if(new_size <= 0)
|
|
{
|
|
IL_SetCacheSize(0);
|
|
net_ReduceMemoryCacheTo(0);
|
|
net_MaxMemoryCacheSize = 0;
|
|
return;
|
|
}
|
|
|
|
/* the netlib mem cache is set to .3*new_size
|
|
* or 200K + .05 * new_size, whichever is less.
|
|
* and imagelb uses the rest
|
|
*/
|
|
if(new_size * .3 < HTML_CACHE_MAX_BASE_SIZE)
|
|
{
|
|
/* use .3 of new_size */
|
|
html_cache_size = (int32) (.3 * new_size);
|
|
}
|
|
else
|
|
{
|
|
/* use HTML_CACHE_MAX_BASE_SIZE + .05*new_size */
|
|
html_cache_size = HTML_CACHE_MAX_BASE_SIZE +
|
|
(int32)(HTML_CACHE_VARIABLE_GROWTH*
|
|
(new_size - HTML_CACHE_MAX_BASE_SIZE));
|
|
}
|
|
|
|
image_cache_size = new_size - html_cache_size;
|
|
|
|
net_MaxMemoryCacheSize = html_cache_size;
|
|
|
|
net_ReduceMemoryCacheTo((uint32) html_cache_size);
|
|
|
|
/* set the image cache to be the rest
|
|
*/
|
|
IL_SetCacheSize(image_cache_size);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Remove the last memory cache object if one exists
|
|
* Returns the total size of the memory cache in bytes
|
|
* after performing the operation
|
|
*/
|
|
PUBLIC int32
|
|
NET_RemoveLastMemoryCacheObject()
|
|
{
|
|
net_remove_last_memory_cache_object();
|
|
|
|
return(net_MemoryCacheSize);
|
|
}
|
|
|
|
/* returns the number of bytes currently in use by the Memory cache
|
|
*/
|
|
PUBLIC int32
|
|
NET_GetMemoryCacheSize()
|
|
{
|
|
return(net_MemoryCacheSize);
|
|
}
|
|
|
|
PUBLIC int32
|
|
NET_GetMaxMemoryCacheSize()
|
|
{
|
|
return net_MaxMemoryCacheSize;
|
|
}
|
|
|
|
/* compare entries for the hashing functions
|
|
*
|
|
* return 0 if match or positive or negative on
|
|
* non match
|
|
*/
|
|
PRIVATE int net_CacheHashComp(net_MemoryCacheObject * obj1,
|
|
net_MemoryCacheObject * obj2)
|
|
{
|
|
int result;
|
|
char *hash1;
|
|
char *hash2;
|
|
char *ques1=0;
|
|
char *ques2=0;
|
|
char *ques3=0;
|
|
char *ques4=0;
|
|
|
|
if(obj1->cache_obj.method != obj2->cache_obj.method)
|
|
return(obj1->cache_obj.method - obj2->cache_obj.method);
|
|
|
|
/* If this is a "news:" or "snews:" URL, then any search data in the URL
|
|
* (the stuff after '?') should be ignored for cache-comparison purposes,
|
|
* so that "news:MSGID" and "news:MSGID?headers=all" share the same cache
|
|
* file.
|
|
*/
|
|
if((NET_TO_UPPER(obj1->cache_obj.address[0]) == 'N'
|
|
|| NET_TO_UPPER(obj1->cache_obj.address[0]) == 'S')
|
|
&& NET_URL_Type(obj1->cache_obj.address) == NEWS_TYPE_URL)
|
|
{
|
|
ques1 = PL_strchr(obj1->cache_obj.address, '?');
|
|
if(ques1)
|
|
*ques1 = '\0';
|
|
}
|
|
|
|
if((NET_TO_UPPER(obj2->cache_obj.address[0]) == 'N'
|
|
|| NET_TO_UPPER(obj2->cache_obj.address[0]) == 'S')
|
|
&& NET_URL_Type(obj2->cache_obj.address) == NEWS_TYPE_URL)
|
|
{
|
|
ques2 = PL_strchr(obj2->cache_obj.address, '?');
|
|
if(ques2)
|
|
*ques2 = '\0';
|
|
}
|
|
|
|
|
|
/* do the same for IMAP */
|
|
if(!PL_strncasecmp(obj1->cache_obj.address,"mailbox://",10))
|
|
{
|
|
ques3 = PL_strstr(obj1->cache_obj.address, "&part=");
|
|
if(ques3)
|
|
*ques3 = '\0';
|
|
}
|
|
|
|
if(!PL_strncasecmp(obj2->cache_obj.address,"mailbox://",10))
|
|
{
|
|
ques4 = PL_strstr(obj2->cache_obj.address, "&part=");
|
|
if(ques4)
|
|
*ques4 = '\0';
|
|
}
|
|
|
|
|
|
/* strip hash symbols because they
|
|
* really represent the same
|
|
* document
|
|
*/
|
|
hash1 = PL_strchr(obj1->cache_obj.address, '#');
|
|
hash2 = PL_strchr(obj2->cache_obj.address, '#');
|
|
|
|
if(hash1)
|
|
*hash1 = '\0';
|
|
if(hash2)
|
|
*hash2 = '\0';
|
|
|
|
result = PL_strcmp(obj1->cache_obj.address, obj2->cache_obj.address);
|
|
|
|
/* set them back to previous values
|
|
*/
|
|
if(hash1)
|
|
*hash1 = '#';
|
|
if(hash2)
|
|
*hash2 = '#';
|
|
if(ques1)
|
|
*ques1 = '?';
|
|
if(ques2)
|
|
*ques2 = '?';
|
|
if(ques3)
|
|
*ques3 = '&';
|
|
if(ques4)
|
|
*ques4 = '&';
|
|
|
|
|
|
if(result != 0)
|
|
return(result);
|
|
|
|
if(!obj1->cache_obj.post_data && !obj2->cache_obj.post_data)
|
|
return(0); /* match; no post data on either */
|
|
|
|
if(obj1->cache_obj.post_data && obj2->cache_obj.post_data)
|
|
{
|
|
result = PL_strcmp(obj1->cache_obj.post_data,
|
|
obj2->cache_obj.post_data);
|
|
return(result);
|
|
}
|
|
else
|
|
{ /* one but not the other */
|
|
if(obj1->cache_obj.post_data)
|
|
return(1);
|
|
else
|
|
return(-1);
|
|
}
|
|
|
|
return(0); /* match with post data */
|
|
}
|
|
|
|
/* hashing function for url's
|
|
*
|
|
* This is some magic Jamie gave me...
|
|
*/
|
|
PRIVATE uint32 net_CacheHashFunc(net_MemoryCacheObject * obj1)
|
|
{
|
|
unsigned const char *x;
|
|
uint32 h = 0;
|
|
uint32 g;
|
|
XP_Bool news_type_url = FALSE;
|
|
XP_Bool imap_type_url = FALSE;
|
|
|
|
if(!obj1)
|
|
return 0;
|
|
|
|
x = (unsigned const char *) obj1->cache_obj.address;
|
|
|
|
/* figure out if it's a news type URL */
|
|
if((NET_TO_UPPER(obj1->cache_obj.address[0]) == 'N'
|
|
|| NET_TO_UPPER(obj1->cache_obj.address[0]) == 'S')
|
|
&& NET_URL_Type(obj1->cache_obj.address) == NEWS_TYPE_URL)
|
|
news_type_url = TRUE;
|
|
/* figure out if it's an IMAP type URL */
|
|
else if (NET_TO_UPPER(obj1->cache_obj.address[0]) == 'M' &&
|
|
!PL_strncasecmp(obj1->cache_obj.address,"Mailbox://",10))
|
|
imap_type_url = TRUE;
|
|
|
|
/* modify the default String hash function
|
|
* to work with URL's
|
|
*/
|
|
assert(x);
|
|
|
|
if (!x) return 0;
|
|
|
|
/* ignore '#' data for all URL's.
|
|
* ignore '?' data for news URL's
|
|
* ignore '&' data for all IMAP URL's
|
|
*/
|
|
while (*x != 0 && *x != '#' && (!news_type_url || *x != '?') &&
|
|
(!imap_type_url || *x != '&'))
|
|
{
|
|
h = (h << 4) + *x++;
|
|
if ((g = h & 0xf0000000) != 0)
|
|
h = (h ^ (g >> 24)) ^ g;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Cache Converter Stream input routines
|
|
*/
|
|
|
|
/* is the stream ready for writing?
|
|
*/
|
|
PRIVATE unsigned int net_MemCacheWriteReady (NET_StreamClass *stream)
|
|
{
|
|
CacheDataObject *obj=stream->data_object;
|
|
if(obj->next_stream)
|
|
return((*obj->next_stream->is_write_ready)(obj->next_stream));
|
|
else
|
|
return(MAX_WRITE_READY);
|
|
}
|
|
|
|
/* stream write function
|
|
*
|
|
* this function accepts a stream and writes the data
|
|
* into memory segments on a segment list.
|
|
*/
|
|
PRIVATE int net_MemCacheWrite (NET_StreamClass *stream, CONST char* buffer, int32 len)
|
|
{
|
|
CacheDataObject *obj=stream->data_object;
|
|
TRACEMSG(("net_MemCacheWrite called with %ld buffer size", len));
|
|
|
|
/* if delete_me is set, we don't want to keep writing to the memcache stream.
|
|
*/
|
|
if(obj->memory_copy && !obj->memory_copy->delete_me)
|
|
{
|
|
net_MemorySegment * mem_seg;
|
|
char * cur_mem_seg_ptr;
|
|
|
|
/* compute a content_length as we read data
|
|
* to use as a comparison and for URL's that
|
|
* don't send a content type
|
|
*/
|
|
obj->computed_content_length += len;
|
|
|
|
/* we are always adding to the end one in the list so
|
|
* lets get it
|
|
*/
|
|
mem_seg = (net_MemorySegment *)
|
|
XP_ListGetEndObject(obj->memory_copy->list);
|
|
PR_ASSERT(mem_seg);
|
|
|
|
if(mem_seg->in_use+len > mem_seg->seg_size)
|
|
{
|
|
/* we need a new segment
|
|
*/
|
|
net_MemorySegment * new_mem_seg = PR_NEW(net_MemorySegment);
|
|
uint32 size_left_in_old_buffer = mem_seg->seg_size - mem_seg->in_use;
|
|
uint32 size_for_new_buffer = len - size_left_in_old_buffer;
|
|
char * new_mem_seg_ptr;
|
|
|
|
if(!new_mem_seg)
|
|
{
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
obj->memory_copy = 0;
|
|
goto EndOfMemWrite;
|
|
}
|
|
|
|
/* @@@ the socket buffer can never be larger
|
|
* than the MAX_MEMORY_ALLOC_SIZE
|
|
*/
|
|
if(size_for_new_buffer > MEMORY_CACHE_SEGMENT_SIZE)
|
|
{
|
|
new_mem_seg->segment = (char*)PR_Malloc(size_for_new_buffer);
|
|
new_mem_seg->seg_size = size_for_new_buffer;
|
|
}
|
|
else
|
|
{
|
|
new_mem_seg->segment = (char*)PR_Malloc(
|
|
MEMORY_CACHE_SEGMENT_SIZE);
|
|
new_mem_seg->seg_size = MEMORY_CACHE_SEGMENT_SIZE;
|
|
}
|
|
|
|
if(!new_mem_seg->segment)
|
|
{
|
|
PR_Free(new_mem_seg);
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
obj->memory_copy = 0;
|
|
goto EndOfMemWrite;
|
|
}
|
|
|
|
/* increase the global cache size counter
|
|
*/
|
|
net_MemoryCacheSize += new_mem_seg->seg_size;
|
|
|
|
TRACEMSG(("Cache size now: %d", net_MemoryCacheSize));
|
|
|
|
cur_mem_seg_ptr = mem_seg->segment;
|
|
new_mem_seg_ptr = new_mem_seg->segment;
|
|
|
|
/* fill in what we can to the old buffer
|
|
*/
|
|
if(size_left_in_old_buffer)
|
|
{
|
|
memcpy(cur_mem_seg_ptr+mem_seg->in_use,
|
|
buffer,
|
|
(size_t) size_left_in_old_buffer);
|
|
mem_seg->in_use = mem_seg->seg_size; /* seg new size */
|
|
}
|
|
|
|
/* fill in the new buffer
|
|
*/
|
|
memcpy(new_mem_seg_ptr,
|
|
buffer+size_left_in_old_buffer,
|
|
(size_t) size_for_new_buffer);
|
|
new_mem_seg->in_use = size_for_new_buffer;
|
|
|
|
net_cache_adding_object++; /* semaphore */
|
|
XP_ListAddObjectToEnd(obj->memory_copy->list, new_mem_seg);
|
|
net_cache_adding_object--; /* semaphore */
|
|
|
|
TRACEMSG(("Adding %d to New memory segment %p", len, new_mem_seg));
|
|
}
|
|
else
|
|
{
|
|
assert (mem_seg->segment);
|
|
cur_mem_seg_ptr = mem_seg->segment;
|
|
|
|
/* fill in some more into the existing segment
|
|
*/
|
|
memcpy(cur_mem_seg_ptr + mem_seg->in_use, buffer, (size_t) len);
|
|
mem_seg->in_use += len;
|
|
|
|
TRACEMSG(("Adding %d to existing memory segment %p", len, mem_seg));
|
|
}
|
|
|
|
}
|
|
|
|
EndOfMemWrite: /* target of a goto from an error above */
|
|
|
|
if(obj->next_stream)
|
|
{
|
|
int status=0;
|
|
|
|
PR_ASSERT (buffer);
|
|
PR_ASSERT (len > -1);
|
|
status = (*obj->next_stream->put_block)
|
|
(obj->next_stream, buffer, len);
|
|
|
|
/* abort */
|
|
if(status < 0)
|
|
return(status);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
/* complete the stream
|
|
*/
|
|
PRIVATE void net_MemCacheComplete (NET_StreamClass *stream)
|
|
{
|
|
CacheDataObject *obj=stream->data_object;
|
|
/* refresh these entries in case meta changes them */
|
|
if(obj->URL_s && obj->memory_copy)
|
|
{
|
|
obj->memory_copy->cache_obj.expires = obj->URL_s->expires;
|
|
obj->memory_copy->cache_obj.last_modified = obj->URL_s->last_modified;
|
|
StrAllocCopy(obj->memory_copy->cache_obj.charset, obj->URL_s->charset);
|
|
}
|
|
|
|
if (obj->memory_copy)
|
|
{
|
|
/* now it's completed */
|
|
obj->memory_copy->completed = TRUE;
|
|
|
|
/* if the object is zero size or
|
|
* if the computed size is different that a given content type,
|
|
* zero the last modified date so that it wont get reused
|
|
* on a recheck or reload
|
|
*/
|
|
if(!obj->computed_content_length
|
|
|| (obj->memory_copy->cache_obj.content_length
|
|
&& obj->memory_copy->cache_obj.content_length
|
|
!= obj->computed_content_length
|
|
)
|
|
)
|
|
{
|
|
obj->memory_copy->cache_obj.last_modified = 0;
|
|
}
|
|
|
|
/* override the given content_length with the correct
|
|
* content length
|
|
*/
|
|
obj->memory_copy->cache_obj.content_length =
|
|
obj->computed_content_length;
|
|
|
|
/* set current time */
|
|
obj->memory_copy->cache_obj.last_accessed = time(NULL);
|
|
}
|
|
|
|
|
|
/* loser: */
|
|
/* complete the next stream
|
|
*/
|
|
if(obj->next_stream)
|
|
{
|
|
(*obj->next_stream->complete)(obj->next_stream);
|
|
PR_Free(obj->next_stream);
|
|
}
|
|
|
|
/* finally, check to see if we want to delete this, after going
|
|
through all that work just to put it in here. */
|
|
if(obj->memory_copy &&
|
|
obj->memory_copy->delete_me &&
|
|
(obj->memory_copy->mem_read_lock == 0))
|
|
{
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
}
|
|
|
|
|
|
/* don't need the data obj any more since this is the end
|
|
* of the stream... */
|
|
PR_REMOVE_LINK(&obj->links);
|
|
PR_Free(obj);
|
|
|
|
/* this is what keeps the cache from growing without end */
|
|
net_ReduceMemoryCacheTo(net_MaxMemoryCacheSize);
|
|
|
|
return;
|
|
}
|
|
|
|
/* add the object into the cache, even if it hasn't been finished
|
|
getting cached. When it's finished, net_MemCacheComplete will
|
|
set the obj->memory_copy->comlpleted flag.
|
|
*/
|
|
PRIVATE void net_MemCacheAddObjectToCache (CacheDataObject * obj)
|
|
{
|
|
int status;
|
|
|
|
/* if the hash table doesn't exist yet, initialize it now
|
|
*/
|
|
if(!net_MemoryCacheHashList)
|
|
{
|
|
|
|
net_MemoryCacheHashList = XP_HashListNew(499,
|
|
(XP_HashingFunction) net_CacheHashFunc,
|
|
(XP_HashCompFunction) net_CacheHashComp);
|
|
if(!net_MemoryCacheHashList)
|
|
{
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
goto loser;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
/* if the delete list doesn't exist yet, initialize it now
|
|
*/
|
|
if(!net_MemoryCacheList)
|
|
{
|
|
net_MemoryCacheList = XP_ListNew();
|
|
if(!net_MemoryCacheList)
|
|
{
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
goto loser;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* memory copy could have been free'd and clear if an
|
|
* error occured in the write, so check to make sure
|
|
* it's still around.
|
|
*/
|
|
if(obj->memory_copy)
|
|
{
|
|
/* set the completed flag so that we know we're in
|
|
* the process of caching this object.
|
|
*/
|
|
obj->memory_copy->completed = FALSE;
|
|
obj->memory_copy->aborted = FALSE;
|
|
|
|
/* if the document requested not to be cached,
|
|
* set the flag to only serve it for reloads and such
|
|
*/
|
|
if(obj->URL_s && obj->URL_s->dont_cache)
|
|
obj->memory_copy->current_page_only = TRUE;
|
|
|
|
/* add the struct to the delete list */
|
|
net_cache_adding_object++; /* semaphore */
|
|
XP_ListAddObject(net_MemoryCacheList, obj->memory_copy);
|
|
net_cache_adding_object--; /* semaphore */
|
|
|
|
/* add the struct to the hash list */
|
|
net_cache_adding_object++; /* semaphore */
|
|
status = XP_HashListAddObject(net_MemoryCacheHashList,
|
|
obj->memory_copy);
|
|
net_cache_adding_object--; /* semaphore */
|
|
|
|
/* check for hash collision */
|
|
if(status == XP_HASH_DUPLICATE_OBJECT)
|
|
{
|
|
net_MemoryCacheObject * tmp_obj;
|
|
|
|
tmp_obj = (net_MemoryCacheObject *)
|
|
XP_HashListFindObject(net_MemoryCacheHashList,
|
|
obj->memory_copy);
|
|
|
|
if ((tmp_obj->mem_read_lock == 0) && tmp_obj->completed)
|
|
{
|
|
/* If there is nobody currently reading or writing the
|
|
* duplicate hash entry, then free the old copy.
|
|
* This will remove it from the hash list, also.
|
|
*/
|
|
net_FreeMemoryCopy(tmp_obj);
|
|
|
|
TRACEMSG(("Found duplicate object in cache. Removing old object"));
|
|
|
|
/* re add the object
|
|
*/
|
|
net_cache_adding_object++; /* semaphore */
|
|
XP_HashListAddObject(net_MemoryCacheHashList,
|
|
obj->memory_copy);
|
|
net_cache_adding_object--; /* semaphore */
|
|
}
|
|
else
|
|
{
|
|
/* We can't cache this entry, the duplicate is
|
|
* currently being read or written. So delete this entry.
|
|
*/
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
}
|
|
}
|
|
}
|
|
|
|
loser:
|
|
|
|
return;
|
|
}
|
|
|
|
/* Abort the stream
|
|
*
|
|
*/
|
|
PRIVATE void net_MemCacheAbort (NET_StreamClass *stream, int status)
|
|
{
|
|
CacheDataObject *obj=stream->data_object;
|
|
/* abort the next stream
|
|
*/
|
|
if(obj->next_stream)
|
|
{
|
|
(*obj->next_stream->abort)(obj->next_stream, status);
|
|
PR_Free(obj->next_stream);
|
|
}
|
|
|
|
/* set the aborted flag so that the object will really be deleted
|
|
* if there are no read locks. */
|
|
obj->memory_copy->aborted = TRUE;
|
|
net_FreeMemoryCopy(obj->memory_copy);
|
|
|
|
PR_REMOVE_LINK(&obj->links);
|
|
PR_Free(obj);
|
|
|
|
return;
|
|
}
|
|
|
|
/* setup the stream
|
|
*/
|
|
MODULE_PRIVATE NET_StreamClass *
|
|
NET_MemCacheConverter (FO_Present_Types format_out,
|
|
void *converter_obj,
|
|
URL_Struct *URL_s,
|
|
MWContext *window_id)
|
|
{
|
|
CacheDataObject * data_object=0;
|
|
NET_StreamClass * stream=0;
|
|
net_MemorySegment * mem_seg=0;
|
|
net_MemoryCacheObject *memory_copy=0;
|
|
net_MemCacheConverterObject *mem_conv_obj = converter_obj;
|
|
|
|
TRACEMSG(("Setting up cache stream. Have URL: %s\n", URL_s->address));
|
|
|
|
TRACEMSG(("Setting up memory cache"));
|
|
|
|
/* check to see if the object is larger than the
|
|
* size of the max memory cache
|
|
*
|
|
* skip the size check if the must_cache flag is set.
|
|
*/
|
|
if(!URL_s->must_cache && (uint32)URL_s->content_length >= net_MaxMemoryCacheSize)
|
|
goto malloc_failure; /* dont cache this URL */
|
|
|
|
/* set up the memory caching struct.
|
|
*
|
|
* malloc the segment first since it's the most likely malloc to fail
|
|
*/
|
|
mem_seg = PR_NEW(net_MemorySegment);
|
|
|
|
if(!mem_seg)
|
|
return(NULL);
|
|
|
|
mem_seg->in_use = 0;
|
|
|
|
if(URL_s->content_length > 0)
|
|
{
|
|
/* use the content_length if we can since that would be
|
|
* the most efficient
|
|
*/
|
|
if(URL_s->content_length > MAX_MEMORY_ALLOC_SIZE)
|
|
{
|
|
mem_seg->segment = (char*)PR_Malloc(MAX_MEMORY_ALLOC_SIZE);
|
|
mem_seg->seg_size = MAX_MEMORY_ALLOC_SIZE;
|
|
}
|
|
else
|
|
{
|
|
/* add 10 just in case it's needed due to a bad content type :) */
|
|
mem_seg->segment = (char*)PR_Malloc(URL_s->content_length+10);
|
|
mem_seg->seg_size = URL_s->content_length+10;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* this is a case of no content length, use standard size segments */
|
|
mem_seg->segment = (char*)PR_Malloc(MEMORY_CACHE_SEGMENT_SIZE);
|
|
mem_seg->seg_size = MEMORY_CACHE_SEGMENT_SIZE;
|
|
}
|
|
|
|
if(!mem_seg->segment)
|
|
{
|
|
PR_Free(mem_seg);
|
|
goto malloc_failure; /* skip memory cacheing */
|
|
}
|
|
|
|
/* malloc the main cache holding structure */
|
|
memory_copy = PR_NEW(net_MemoryCacheObject);
|
|
if(!memory_copy)
|
|
{
|
|
PR_Free(mem_seg);
|
|
PR_Free(mem_seg->segment);
|
|
goto malloc_failure; /* skip memory cacheing */
|
|
}
|
|
memset(memory_copy, 0, sizeof(net_MemoryCacheObject));
|
|
|
|
memory_copy->list = XP_ListNew();
|
|
if(!memory_copy->list)
|
|
{
|
|
PR_Free(mem_seg);
|
|
PR_Free(mem_seg->segment);
|
|
PR_Free(memory_copy);
|
|
goto malloc_failure; /* skip memory cacheing */
|
|
}
|
|
|
|
/* if we get this far add the object to the size count
|
|
* since if we fail anywhere below here it will get
|
|
* subtracted by net_FreeMemoryCopy(memory_copy);
|
|
*/
|
|
net_MemoryCacheSize += mem_seg->seg_size;
|
|
|
|
/* add the segment malloced above to the segment list */
|
|
net_cache_adding_object++; /* semaphore */
|
|
XP_ListAddObject(memory_copy->list, mem_seg);
|
|
net_cache_adding_object--; /* semaphore */
|
|
|
|
StrAllocCopy(memory_copy->cache_obj.address, URL_s->address);
|
|
memory_copy->cache_obj.method = URL_s->method;
|
|
if(URL_s->post_data)
|
|
{
|
|
memory_copy->cache_obj.post_data_size = URL_s->post_data_size;
|
|
StrAllocCopy(memory_copy->cache_obj.post_data, URL_s->post_data);
|
|
StrAllocCopy(memory_copy->cache_obj.post_headers, URL_s->post_headers);
|
|
}
|
|
|
|
memory_copy->cache_obj.expires = URL_s->expires;
|
|
memory_copy->cache_obj.last_modified = URL_s->last_modified;
|
|
|
|
memory_copy->cache_obj.content_length = URL_s->content_length;
|
|
memory_copy->cache_obj.real_content_length = URL_s->content_length;
|
|
memory_copy->cache_obj.is_netsite = URL_s->is_netsite;
|
|
|
|
StrAllocCopy(memory_copy->cache_obj.content_type, URL_s->content_type);
|
|
StrAllocCopy(memory_copy->cache_obj.charset, URL_s->charset);
|
|
StrAllocCopy(memory_copy->cache_obj.content_encoding,
|
|
URL_s->content_encoding);
|
|
|
|
/* check for malloc failure on critical fields above */
|
|
if(!memory_copy->cache_obj.address || !memory_copy->cache_obj.content_type)
|
|
{
|
|
net_FreeMemoryCopy(memory_copy);
|
|
goto malloc_failure; /* skip memory cacheing */
|
|
}
|
|
|
|
HG83363
|
|
|
|
/* build the stream object */
|
|
stream = PR_NEW(NET_StreamClass);
|
|
if(!stream)
|
|
{
|
|
net_FreeMemoryCopy(memory_copy);
|
|
goto malloc_failure; /* skip memory cacheing */
|
|
}
|
|
|
|
/* this structure gets passed back into the write, complete
|
|
* and abort stream functions
|
|
*/
|
|
data_object = PR_NEW(CacheDataObject);
|
|
if (!data_object)
|
|
{
|
|
PR_Free(stream);
|
|
net_FreeMemoryCopy(memory_copy);
|
|
goto malloc_failure; /* skip memory cacheing */
|
|
}
|
|
|
|
/* init the object */
|
|
memset(data_object, 0, sizeof(CacheDataObject));
|
|
|
|
/* assign the cache object to the stream data object
|
|
*/
|
|
data_object->memory_copy = memory_copy;
|
|
|
|
if (mem_conv_obj->dont_hold_URL_s == FALSE)
|
|
data_object->URL_s = URL_s;
|
|
|
|
TRACEMSG(("Returning stream from NET_CacheConverter\n"));
|
|
|
|
stream->name = "Cache stream";
|
|
stream->complete = (MKStreamCompleteFunc) net_MemCacheComplete;
|
|
stream->abort = (MKStreamAbortFunc) net_MemCacheAbort;
|
|
stream->put_block = (MKStreamWriteFunc) net_MemCacheWrite;
|
|
stream->is_write_ready = (MKStreamWriteReadyFunc) net_MemCacheWriteReady;
|
|
stream->data_object = data_object; /* document info object */
|
|
stream->window_id = window_id;
|
|
|
|
/* next stream is passed in by the caller but can be null */
|
|
data_object->next_stream = mem_conv_obj->next_stream;
|
|
|
|
PR_APPEND_LINK(&data_object->links, &mem_active_cache_data_objects);
|
|
|
|
/* add the cache object to the hash list, but don't
|
|
* complete it yet
|
|
*/
|
|
net_MemCacheAddObjectToCache(data_object);
|
|
|
|
return stream;
|
|
|
|
malloc_failure: /* target of malloc failure */
|
|
|
|
TRACEMSG(("NOT Caching this URL"));
|
|
|
|
/* bypass the whole cache mechanism
|
|
*/
|
|
if(format_out != FO_CACHE_ONLY)
|
|
{
|
|
format_out = CLEAR_CACHE_BIT(format_out);
|
|
return(mem_conv_obj->next_stream);
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
PRIVATE net_MemoryCacheObject *
|
|
net_FindObjectInMemoryCache(URL_Struct *URL_s)
|
|
{
|
|
net_MemoryCacheObject tmp_cache_obj, *return_obj;
|
|
|
|
/* fill in the temporary cache object so we can
|
|
* use it for searching
|
|
*/
|
|
memset(&tmp_cache_obj, 0, sizeof(tmp_cache_obj));
|
|
tmp_cache_obj.cache_obj.method = URL_s->method;
|
|
tmp_cache_obj.cache_obj.address = URL_s->address;
|
|
tmp_cache_obj.cache_obj.post_data = URL_s->post_data;
|
|
tmp_cache_obj.cache_obj.post_data_size = URL_s->post_data_size;
|
|
|
|
return_obj = (net_MemoryCacheObject *)
|
|
XP_HashListFindObject(net_MemoryCacheHashList, &tmp_cache_obj);
|
|
if (return_obj)
|
|
return (return_obj);
|
|
else
|
|
{
|
|
/* try unescaping the URL address - MHTML parts are
|
|
escaped, and weren't getting picked up in the
|
|
cache */
|
|
tmp_cache_obj.cache_obj.address = PL_strdup(URL_s->address);
|
|
if (tmp_cache_obj.cache_obj.address)
|
|
{
|
|
/* unescape it */
|
|
NET_UnEscape(tmp_cache_obj.cache_obj.address);
|
|
if (tmp_cache_obj.cache_obj.address)
|
|
{
|
|
return_obj = (net_MemoryCacheObject *)
|
|
XP_HashListFindObject(net_MemoryCacheHashList, &tmp_cache_obj);
|
|
PR_Free(tmp_cache_obj.cache_obj.address);
|
|
}
|
|
}
|
|
return (return_obj);
|
|
}
|
|
}
|
|
|
|
/* set or unset a lock on a memory cache object
|
|
*/
|
|
MODULE_PRIVATE void
|
|
NET_ChangeMemCacheLock(URL_Struct *URL_s, PRBool set)
|
|
{
|
|
net_MemoryCacheObject * found_cache_obj;
|
|
|
|
/* look up cache struct */
|
|
found_cache_obj = net_FindObjectInMemoryCache(URL_s);
|
|
|
|
if(found_cache_obj && found_cache_obj->completed)
|
|
{
|
|
PR_ASSERT(found_cache_obj->external_locks >= 0);
|
|
|
|
if(set)
|
|
{
|
|
/* increment lock counter */
|
|
found_cache_obj->external_locks++;
|
|
}
|
|
else
|
|
{
|
|
/* decrement lock counter */
|
|
found_cache_obj->external_locks--;
|
|
|
|
/* PR_ASSERT(found_cache_obj->external_locks >= 0); */
|
|
|
|
if(found_cache_obj->external_locks < 0)
|
|
found_cache_obj->external_locks = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remove a URL from the memory cache
|
|
*/
|
|
MODULE_PRIVATE void
|
|
NET_RemoveURLFromMemCache(URL_Struct *URL_s)
|
|
{
|
|
net_MemoryCacheObject * found_cache_obj;
|
|
|
|
found_cache_obj = net_FindObjectInMemoryCache(URL_s);
|
|
|
|
if(found_cache_obj &&
|
|
(found_cache_obj->mem_read_lock == 0) &&
|
|
found_cache_obj->completed)
|
|
net_FreeMemoryCopy(found_cache_obj);
|
|
}
|
|
|
|
/* Returns TRUE if the URL is currently in the
|
|
* memory cache and false otherwise.
|
|
*/
|
|
PUBLIC XP_Bool
|
|
NET_IsURLInMemCache(URL_Struct *URL_s)
|
|
{
|
|
if(net_FindObjectInMemoryCache(URL_s))
|
|
return(TRUE);
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
/* this function looks up a URL in the hash table and
|
|
* returns MEMORY_CACHE_TYPE_URL if it's found and
|
|
* should be loaded via the memory cache.
|
|
*
|
|
* It also sets several entries in the passed in URL
|
|
* struct including "memory_copy" which is a pointer
|
|
* to the found net_MemoryCacheObject struct.
|
|
*/
|
|
MODULE_PRIVATE int
|
|
NET_FindURLInMemCache(URL_Struct * URL_s, MWContext *ctxt)
|
|
#ifdef NU_CACHE
|
|
{
|
|
PR_ASSERT(0); /* Should not be getting called. Also, compiler will warn of missing prototype */
|
|
return 0;
|
|
}
|
|
#else
|
|
{
|
|
net_MemoryCacheObject *found_cache_obj;
|
|
|
|
TRACEMSG(("Checking for URL in cache"));
|
|
|
|
if(!net_MemoryCacheHashList)
|
|
return(0);
|
|
|
|
found_cache_obj = net_FindObjectInMemoryCache(URL_s);
|
|
|
|
if(found_cache_obj)
|
|
{
|
|
TRACEMSG(("mkcache: found URL in memory cache!"));
|
|
|
|
if(found_cache_obj->current_page_only)
|
|
{
|
|
History_entry * he = SHIST_GetCurrent(&ctxt->hist);
|
|
int hist_num;
|
|
|
|
/* if the current_page_only flag is set
|
|
* then the page should only be
|
|
* served if it's the current document loaded
|
|
* (i.e. reloads)
|
|
*
|
|
* @@@ if !he then let it through, this allows the
|
|
* doc info window to work
|
|
*/
|
|
if(he)
|
|
{
|
|
hist_num = SHIST_GetIndex(&ctxt->hist, he);
|
|
|
|
/* we can tell if the document being loaded is the
|
|
* same as the current document by looking up it's
|
|
* index in the history. If they don't match
|
|
* then it isn't the current document
|
|
*/
|
|
if(URL_s->history_num != hist_num)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* set a pointer to the structure in the URL struct
|
|
*/
|
|
URL_s->memory_copy = found_cache_obj;
|
|
|
|
/* copy the contents of the URL struct so that the content type
|
|
* and other stuff gets recorded
|
|
*/
|
|
StrAllocCopy(URL_s->content_type,
|
|
found_cache_obj->cache_obj.content_type);
|
|
StrAllocCopy(URL_s->charset,
|
|
found_cache_obj->cache_obj.charset);
|
|
StrAllocCopy(URL_s->content_encoding,
|
|
found_cache_obj->cache_obj.content_encoding);
|
|
URL_s->content_length = found_cache_obj->cache_obj.content_length;
|
|
URL_s->real_content_length = found_cache_obj->cache_obj.content_length;
|
|
URL_s->expires = found_cache_obj->cache_obj.expires;
|
|
URL_s->last_modified = found_cache_obj->cache_obj.last_modified;
|
|
URL_s->is_netsite = found_cache_obj->cache_obj.is_netsite;
|
|
|
|
HG26557
|
|
net_cache_adding_object++; /* semaphore */
|
|
/* reorder objects so that the list is in last accessed order */
|
|
XP_ListRemoveObject(net_MemoryCacheList, found_cache_obj);
|
|
XP_ListAddObject(net_MemoryCacheList, found_cache_obj);
|
|
net_cache_adding_object--; /* semaphore */
|
|
|
|
TIMING_MESSAGE(("cache,%s,in memory", URL_s->address));
|
|
TRACEMSG(("Cached copy is valid. returning method"));
|
|
|
|
return(MEMORY_CACHE_TYPE_URL);
|
|
}
|
|
|
|
TRACEMSG(("URL not found in cache"));
|
|
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************
|
|
* Memory cache output module routine
|
|
*
|
|
* This set of routines pushes the document in memory up a stream
|
|
* created by NET_StreamBuilder
|
|
*/
|
|
|
|
/* used to hold data between invokations of ProcessNet
|
|
*/
|
|
typedef struct _MemCacheConData {
|
|
XP_List *cur_list_ptr;
|
|
uint32 bytes_written_in_segment;
|
|
NET_StreamClass *stream;
|
|
} MemCacheConData;
|
|
|
|
#define CD_CUR_LIST_PTR connection_data->cur_list_ptr
|
|
#define CD_BYTES_WRITTEN_IN_SEGMENT connection_data->bytes_written_in_segment
|
|
#define CD_STREAM connection_data->stream
|
|
|
|
#define CE_URL_S cur_entry->URL_s
|
|
#define CE_WINDOW_ID cur_entry->window_id
|
|
#define CE_FORMAT_OUT cur_entry->format_out
|
|
#define CE_STATUS cur_entry->status
|
|
#define CE_BYTES_RECEIVED cur_entry->bytes_received
|
|
|
|
/* begin the load, This is called from NET_GetURL
|
|
*/
|
|
#ifdef OLD_MOZ_MAIL_NEWS
|
|
extern int
|
|
net_InitializeNewsFeData (ActiveEntry * cur_entry);
|
|
extern int
|
|
IMAP_InitializeImapFeData (ActiveEntry * cur_entry);
|
|
extern void
|
|
IMAP_URLFinished(URL_Struct *URL_s);
|
|
#endif /* OLD_MOZ_MAIL_NEWS */
|
|
|
|
PRIVATE int32
|
|
net_MemoryCacheLoad (ActiveEntry * cur_entry)
|
|
#ifdef NU_CACHE
|
|
{
|
|
PR_ASSERT(0); /* Should not be getting called */
|
|
return 0;
|
|
}
|
|
#else
|
|
{
|
|
/* get memory for Connection Data */
|
|
MemCacheConData * connection_data = PR_NEW(MemCacheConData);
|
|
net_MemorySegment * mem_seg;
|
|
uint32 chunk_size;
|
|
char *mem_seg_ptr;
|
|
char *first_buffer;
|
|
|
|
TRACEMSG(("Entering NET_MemoryCacheLoad!\n"));
|
|
|
|
if(!connection_data)
|
|
{
|
|
CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
|
|
CE_STATUS = MK_OUT_OF_MEMORY;
|
|
return (CE_STATUS);
|
|
}
|
|
|
|
if (!CE_URL_S->memory_copy)
|
|
{
|
|
/* the memory_copy has been freed before fully
|
|
* making it into the cache. In other words, it's
|
|
* been aborted. Abort this load then, also.
|
|
* We should probably never hit this case, however.
|
|
* If we got here, it means we've just found this entry
|
|
* in the hash table, which means we have not deleted it
|
|
* and we have not set the delete_me flag.
|
|
*/
|
|
PR_ASSERT(FALSE);
|
|
CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
|
|
return (CE_STATUS);
|
|
}
|
|
|
|
cur_entry->protocol = MEMORY_CACHE_TYPE_URL;
|
|
cur_entry->memory_file = TRUE;
|
|
|
|
/* point to the first list struct that contains data
|
|
*/
|
|
CD_CUR_LIST_PTR = CE_URL_S->memory_copy->list->next;
|
|
CD_BYTES_WRITTEN_IN_SEGMENT = 0;
|
|
|
|
/* put a read lock on the data
|
|
*/
|
|
CE_URL_S->memory_copy->mem_read_lock++;
|
|
|
|
cur_entry->con_data = connection_data;
|
|
|
|
cur_entry->local_file = TRUE;
|
|
|
|
cur_entry->socket = NULL;
|
|
|
|
NET_SetCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
|
|
|
|
cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
|
|
|
|
FE_EnableClicking(CE_WINDOW_ID);
|
|
|
|
#ifdef OLD_MOZ_MAIL_NEWS
|
|
if (cur_entry->format_out == FO_PRESENT)
|
|
{
|
|
if (NET_URL_Type(cur_entry->URL_s->address) == NEWS_TYPE_URL)
|
|
{
|
|
/* #### DISGUSTING KLUDGE to make cacheing work for news articles. */
|
|
cur_entry->status = net_InitializeNewsFeData (cur_entry);
|
|
if (cur_entry->status < 0)
|
|
{
|
|
/* #### what error message? */
|
|
return cur_entry->status;
|
|
}
|
|
}
|
|
else if (!PL_strncmp(CE_URL_S->address, "Mailbox://", 10))
|
|
{
|
|
/* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
|
|
cur_entry->status = IMAP_InitializeImapFeData (cur_entry);
|
|
if (cur_entry->status < 0)
|
|
{
|
|
/* #### what error message? */
|
|
return cur_entry->status;
|
|
}
|
|
}
|
|
}
|
|
#endif /* OLD_MOZ_MAIL_NEWS */
|
|
|
|
/* open the outgoing stream
|
|
*/
|
|
CD_STREAM = NET_StreamBuilder(CE_FORMAT_OUT, CE_URL_S, CE_WINDOW_ID);
|
|
if(!CD_STREAM)
|
|
{
|
|
NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
|
|
|
|
PR_Free(connection_data);
|
|
|
|
CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
|
|
CE_STATUS = MK_UNABLE_TO_CONVERT;
|
|
return (CE_STATUS);
|
|
}
|
|
|
|
if (!CE_URL_S->load_background)
|
|
FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
|
|
|
|
/* process one chunk of the
|
|
* cache file so that
|
|
* layout can continue
|
|
* when images are in the cache
|
|
*/
|
|
#define FIRST_BUFF_SIZE 1024
|
|
if (CE_URL_S->memory_copy->completed)
|
|
{
|
|
mem_seg = (net_MemorySegment *) CD_CUR_LIST_PTR->object;
|
|
|
|
mem_seg_ptr = mem_seg->segment;
|
|
|
|
chunk_size = MIN(FIRST_BUFF_SIZE,
|
|
mem_seg->in_use-CD_BYTES_WRITTEN_IN_SEGMENT);
|
|
|
|
/* malloc this first buffer because we can't use
|
|
* the NET_SocketBuffer in calls from NET_GetURL
|
|
* because of reentrancy
|
|
*/
|
|
first_buffer = (char *) PR_Malloc(chunk_size);
|
|
|
|
if(!first_buffer)
|
|
{
|
|
PR_Free(connection_data);
|
|
CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
|
|
/* copy the segment because the parser will muck with it
|
|
*/
|
|
memcpy(first_buffer,
|
|
mem_seg_ptr+CD_BYTES_WRITTEN_IN_SEGMENT,
|
|
(size_t) chunk_size);
|
|
|
|
CD_BYTES_WRITTEN_IN_SEGMENT += chunk_size;
|
|
|
|
CE_STATUS = (*CD_STREAM->put_block)(CD_STREAM,
|
|
first_buffer,
|
|
chunk_size);
|
|
if(CE_STATUS < 0)
|
|
{
|
|
NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
|
|
|
|
if (!CE_URL_S->load_background)
|
|
FE_GraphProgressDestroy(CE_WINDOW_ID,
|
|
CE_URL_S,
|
|
CE_URL_S->content_length,
|
|
CE_BYTES_RECEIVED);
|
|
|
|
PR_Free(connection_data);
|
|
|
|
return (CE_STATUS);
|
|
}
|
|
|
|
CE_BYTES_RECEIVED += chunk_size;
|
|
|
|
/* check to see if we need to advance the pointer yet.
|
|
* should hardly ever happen here since the first buffer
|
|
* is so small
|
|
*/
|
|
if(CD_BYTES_WRITTEN_IN_SEGMENT >= mem_seg->in_use)
|
|
{
|
|
CD_CUR_LIST_PTR = CD_CUR_LIST_PTR->next;
|
|
CD_BYTES_WRITTEN_IN_SEGMENT = 0;
|
|
}
|
|
|
|
PR_Free(first_buffer);
|
|
}
|
|
else
|
|
{
|
|
CE_STATUS = 0;
|
|
}
|
|
|
|
return(CE_STATUS);
|
|
|
|
}
|
|
#endif /* NU_CACHE */
|
|
|
|
/* called repeatedly from NET_ProcessNet to push all the
|
|
* data up the stream
|
|
*/
|
|
PRIVATE int32
|
|
net_ProcessMemoryCache (ActiveEntry * cur_entry)
|
|
#ifdef NU_CACHE
|
|
{
|
|
PR_ASSERT(0); /* Should not be getting called */
|
|
return 0;
|
|
}
|
|
#else
|
|
{
|
|
MemCacheConData * connection_data = (MemCacheConData *)cur_entry->con_data;
|
|
net_MemorySegment * mem_seg;
|
|
uint32 chunk_size;
|
|
uint32 buffer_size;
|
|
char *mem_seg_ptr;
|
|
|
|
|
|
if (!CE_URL_S->memory_copy)
|
|
{
|
|
/* the memory_copy has been freed before fully
|
|
* making it into the cache. In other words, it's
|
|
* been aborted. Abort this load then, also,
|
|
* since it's a concurrent load.
|
|
* We know that CE_URL_S->memory_copy will be NULL
|
|
* if it's been removed, because we have a read lock
|
|
* on the entry, and that will cause the delete_me
|
|
* flag to be set, rather than just deleting the
|
|
* object initially. We catch the delete_me flag
|
|
* here, and set CE_URL_S->memory_copy to NULL.
|
|
*/
|
|
CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
|
|
return (CE_STATUS);
|
|
}
|
|
|
|
|
|
/* wait until the object has been fully inserted into
|
|
* the cache.
|
|
*/
|
|
if (!CE_URL_S->memory_copy->completed &&
|
|
!(CE_URL_S->memory_copy->delete_me &&
|
|
CE_URL_S->memory_copy->aborted))
|
|
{
|
|
/* normal case - we are still waiting for it to complete */
|
|
return 0;
|
|
}
|
|
|
|
if(!CD_CUR_LIST_PTR ||
|
|
(CE_URL_S->memory_copy->aborted &&
|
|
CE_URL_S->memory_copy->delete_me))
|
|
{
|
|
/* when CD_CUR_LIST_PTR turns NULL we are at the end
|
|
* of the document. Finish up the stream and exit.
|
|
*
|
|
* If the entry is aborted and the delete_me flag is set,
|
|
* the stream was aborted in the middle of being written.
|
|
* If we are here, then it's simultaneously being read also.
|
|
* So, finish up.
|
|
*/
|
|
|
|
/* complete the stream
|
|
*/
|
|
(*CD_STREAM->complete)(CD_STREAM);
|
|
|
|
/* free the stream
|
|
*/
|
|
PR_Free(CD_STREAM);
|
|
|
|
/* remove the read lock
|
|
*/
|
|
CE_URL_S->memory_copy->mem_read_lock--;
|
|
|
|
/* check to see if we should delete this memory cached object
|
|
*
|
|
* delete_me is set by FreeMemoryCacheObject when it tried
|
|
* to free it but couldn't because of read locks on the
|
|
* object
|
|
*/
|
|
if(CE_URL_S->memory_copy->delete_me &&
|
|
CE_URL_S->memory_copy->mem_read_lock == 0)
|
|
{
|
|
net_FreeMemoryCopy(CE_URL_S->memory_copy);
|
|
CE_URL_S->memory_copy = 0;
|
|
}
|
|
|
|
/* PR_Free the structs used by this protocol module
|
|
*/
|
|
PR_Free(connection_data);
|
|
|
|
/* set the status to success */
|
|
if (CE_URL_S->memory_copy && CE_URL_S->memory_copy->completed)
|
|
CE_STATUS = MK_DATA_LOADED;
|
|
else
|
|
CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
|
|
|
|
/* clear the CallNetlibAllTheTime if there are no
|
|
* other readers
|
|
*/
|
|
NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
|
|
|
|
if (!CE_URL_S->load_background)
|
|
FE_GraphProgressDestroy(CE_WINDOW_ID,
|
|
CE_URL_S,
|
|
CE_URL_S->content_length,
|
|
CE_BYTES_RECEIVED);
|
|
|
|
#ifdef OLD_MOZ_MAIL_NEWS
|
|
if (!PL_strncmp(CE_URL_S->address, "Mailbox://", 10))
|
|
/* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
|
|
IMAP_URLFinished(CE_URL_S);
|
|
#endif
|
|
/* Tell ProcessNet that we are all done */
|
|
return(-1);
|
|
}
|
|
|
|
/* CD_CUR_LIST_PTR is pointing to the most current
|
|
* memory segment list object
|
|
*/
|
|
mem_seg = (net_MemorySegment *) CD_CUR_LIST_PTR->object;
|
|
|
|
TRACEMSG(("ProcessMemoryCache: printing segment %p",mem_seg));
|
|
|
|
mem_seg_ptr = mem_seg->segment;
|
|
|
|
/* write out at least part of the buffer
|
|
*/
|
|
buffer_size = (*CD_STREAM->is_write_ready)(CD_STREAM);
|
|
buffer_size = MIN(buffer_size, (unsigned int) NET_Socket_Buffer_Size);
|
|
|
|
/* make it ??? at the most
|
|
* when coming out of the cache
|
|
*/
|
|
buffer_size = MIN(buffer_size, NET_MEM_CACHE_OUTPUT_SIZE);
|
|
|
|
chunk_size = MIN(buffer_size, mem_seg->in_use-CD_BYTES_WRITTEN_IN_SEGMENT);
|
|
|
|
/* copy the segment because the parser will muck with it
|
|
*/
|
|
memcpy(NET_Socket_Buffer,
|
|
mem_seg_ptr+CD_BYTES_WRITTEN_IN_SEGMENT,
|
|
(size_t) chunk_size);
|
|
|
|
/* remember how much of this segment we have written */
|
|
CD_BYTES_WRITTEN_IN_SEGMENT += chunk_size;
|
|
|
|
CE_STATUS = (*CD_STREAM->put_block)(CD_STREAM,
|
|
NET_Socket_Buffer,
|
|
chunk_size);
|
|
CE_BYTES_RECEIVED += chunk_size;
|
|
|
|
/* check to see if we need to advance the pointer yet
|
|
*/
|
|
if(CD_BYTES_WRITTEN_IN_SEGMENT >= mem_seg->in_use)
|
|
{
|
|
CD_CUR_LIST_PTR = CD_CUR_LIST_PTR->next;
|
|
CD_BYTES_WRITTEN_IN_SEGMENT = 0;
|
|
}
|
|
|
|
if (!CE_URL_S->load_background)
|
|
FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED,
|
|
chunk_size, CE_URL_S->content_length);
|
|
|
|
if(CE_STATUS < 0)
|
|
{
|
|
NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
|
|
|
|
(*CD_STREAM->abort)(CD_STREAM, CE_STATUS);
|
|
}
|
|
|
|
return(CE_STATUS);
|
|
|
|
}
|
|
#endif /* NU_CACHE */
|
|
|
|
/* called by functions in mkgeturl to interrupt the loading of
|
|
* an object. (Usually a user interrupt)
|
|
*/
|
|
PRIVATE int32
|
|
net_InterruptMemoryCache (ActiveEntry * cur_entry)
|
|
#ifdef NU_CACHE
|
|
{
|
|
PR_ASSERT(0); /* Should not be getting called */
|
|
return 0;
|
|
}
|
|
#else
|
|
{
|
|
MemCacheConData * connection_data = (MemCacheConData *)cur_entry->con_data;
|
|
|
|
/* abort and free the outgoing stream
|
|
*/
|
|
(*CD_STREAM->abort)(CD_STREAM, MK_INTERRUPTED);
|
|
PR_Free(CD_STREAM);
|
|
|
|
if (!CE_URL_S->memory_copy)
|
|
{
|
|
/* the memory_copy has been freed before fully
|
|
* making it into the cache. In other words, it's
|
|
* been aborted. Abort this load then, also,
|
|
* since it's a concurrent load.
|
|
*/
|
|
CE_STATUS = MK_OBJECT_NOT_IN_CACHE;
|
|
return (CE_STATUS);
|
|
}
|
|
|
|
/* remove the read lock
|
|
*/
|
|
CE_URL_S->memory_copy->mem_read_lock--;
|
|
|
|
/* check to see if we should delete this memory cached object
|
|
*/
|
|
if(CE_URL_S->memory_copy->delete_me &&
|
|
CE_URL_S->memory_copy->mem_read_lock == 0)
|
|
{
|
|
net_FreeMemoryCopy(CE_URL_S->memory_copy);
|
|
CE_URL_S->memory_copy = 0;
|
|
}
|
|
|
|
/* PR_Free the structs
|
|
*/
|
|
PR_Free(connection_data);
|
|
CE_STATUS = MK_INTERRUPTED;
|
|
|
|
NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkmemcac");
|
|
|
|
return(CE_STATUS);
|
|
}
|
|
#endif
|
|
|
|
/* create an HTML stream and push a bunch of HTML about
|
|
* the memory cache
|
|
*/
|
|
MODULE_PRIVATE void
|
|
NET_DisplayMemCacheInfoAsHTML(ActiveEntry * cur_entry)
|
|
{
|
|
char *buffer = (char*)PR_Malloc(2048);
|
|
char *address;
|
|
char *escaped;
|
|
NET_StreamClass * stream;
|
|
net_CacheObject * cache_obj;
|
|
net_MemoryCacheObject * mem_cache_obj;
|
|
XP_Bool long_form = FALSE;
|
|
int32 number_in_memory_cache;
|
|
XP_List *list_ptr;
|
|
int i;
|
|
|
|
if(!buffer)
|
|
{
|
|
cur_entry->status = MK_UNABLE_TO_CONVERT;
|
|
return;
|
|
}
|
|
|
|
if(PL_strcasestr(cur_entry->URL_s->address, "?long"))
|
|
long_form = TRUE;
|
|
else if(PL_strcasestr(cur_entry->URL_s->address, "?traceon"))
|
|
#ifdef NU_CACHE
|
|
CacheTrace_Enable(TRUE);
|
|
#else
|
|
NET_CacheTraceOn = TRUE;
|
|
#endif
|
|
else if(PL_strcasestr(cur_entry->URL_s->address, "?traceoff"))
|
|
#ifdef NU_CACHE
|
|
CacheTrace_Enable(FALSE);
|
|
#else
|
|
NET_CacheTraceOn = FALSE;
|
|
#endif
|
|
|
|
StrAllocCopy(cur_entry->URL_s->content_type, TEXT_HTML);
|
|
|
|
cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
|
|
stream = NET_StreamBuilder(cur_entry->format_out,
|
|
cur_entry->URL_s,
|
|
cur_entry->window_id);
|
|
|
|
if(!stream)
|
|
{
|
|
cur_entry->status = MK_UNABLE_TO_CONVERT;
|
|
PR_Free(buffer);
|
|
return;
|
|
}
|
|
|
|
/* define a macro to push a string up the stream
|
|
* and handle errors
|
|
*/
|
|
#define PUT_PART(part) \
|
|
cur_entry->status = (*stream->put_block)(stream, \
|
|
part ? part : "Unknown", \
|
|
part ? PL_strlen(part) : 7); \
|
|
if(cur_entry->status < 0) \
|
|
goto END;
|
|
|
|
if(!net_MemoryCacheList)
|
|
{
|
|
PL_strcpy(buffer, "There are no objects in the memory cache");
|
|
PUT_PART(buffer);
|
|
goto END;
|
|
}
|
|
|
|
number_in_memory_cache = XP_ListCount(net_MemoryCacheList);
|
|
|
|
/* add the header info */
|
|
sprintf(buffer,
|
|
"<TITLE>Information about the Netscape memory cache</TITLE>\n"
|
|
"<h2>Memory Cache statistics</h2>\n"
|
|
"<TABLE>\n"
|
|
"<TR>\n"
|
|
"<TD ALIGN=RIGHT><b>Maximum size:</TD>\n"
|
|
"<TD>%ld</TD>\n"
|
|
"</TR>\n"
|
|
"<TR>\n"
|
|
"<TD ALIGN=RIGHT><b>Current size:</TD>\n"
|
|
"<TD>%ld</TD>\n"
|
|
"</TR>\n"
|
|
"<TR>\n"
|
|
"<TD ALIGN=RIGHT><b>Number of files in cache:</TD>\n"
|
|
"<TD>%ld</TD>\n"
|
|
"</TR>\n"
|
|
"<TR>\n"
|
|
"<TD ALIGN=RIGHT><b>Average cache file size:</TD>\n"
|
|
"<TD>%ld</TD>\n"
|
|
"</TR>\n"
|
|
"</TABLE>\n"
|
|
"<HR>",
|
|
net_MaxMemoryCacheSize,
|
|
net_MemoryCacheSize,
|
|
number_in_memory_cache,
|
|
number_in_memory_cache ? net_MemoryCacheSize/number_in_memory_cache : 0);
|
|
|
|
PUT_PART(buffer);
|
|
|
|
/* define some macros to help us output HTML tables
|
|
*/
|
|
#if 0
|
|
|
|
#define TABLE_TOP(arg1) \
|
|
sprintf(buffer, \
|
|
"<TR><TD ALIGN=RIGHT><b>%s</TD>\n" \
|
|
"<TD>", arg1); \
|
|
PUT_PART(buffer);
|
|
|
|
#define TABLE_BOTTOM \
|
|
sprintf(buffer, \
|
|
"</TD></TR>"); \
|
|
PUT_PART(buffer);
|
|
|
|
#else
|
|
|
|
#define TABLE_TOP(arg1) \
|
|
PL_strcpy(buffer, "<tt>"); \
|
|
for(i=PL_strlen(arg1); i < 16; i++) \
|
|
PL_strcat(buffer, " "); \
|
|
PL_strcat(buffer, arg1); \
|
|
PL_strcat(buffer, " </tt>"); \
|
|
PUT_PART(buffer);
|
|
|
|
#define TABLE_BOTTOM \
|
|
PL_strcpy(buffer, "<BR>\n"); \
|
|
PUT_PART(buffer);
|
|
|
|
#endif
|
|
|
|
list_ptr = net_MemoryCacheList;
|
|
|
|
while((mem_cache_obj = (net_MemoryCacheObject *) XP_ListNextObject(list_ptr)) != NULL)
|
|
{
|
|
|
|
cache_obj = &mem_cache_obj->cache_obj;
|
|
address = PL_strdup(mem_cache_obj->cache_obj.address);
|
|
|
|
/* put the URL out there */
|
|
TABLE_TOP("URL:");
|
|
PL_strcpy(buffer, "<A TARGET=Internal_URL_Info HREF=\"about:");
|
|
PUT_PART(buffer);
|
|
escaped = NET_EscapeDoubleQuote(address);
|
|
PUT_PART(escaped);
|
|
PR_Free(escaped);
|
|
XP_STRCPY(buffer, "\">");
|
|
PUT_PART(buffer);
|
|
escaped = NET_EscapeHTML(address);
|
|
PUT_PART(escaped);
|
|
PR_Free(address);
|
|
PR_Free(escaped);
|
|
PL_strcpy(buffer, "</A>");
|
|
PUT_PART(buffer);
|
|
TABLE_BOTTOM;
|
|
|
|
TABLE_TOP("Content Length:");
|
|
sprintf(buffer, "%lu", cache_obj->content_length);
|
|
PUT_PART(buffer);
|
|
TABLE_BOTTOM;
|
|
|
|
TABLE_TOP("Content type:");
|
|
PUT_PART(cache_obj->content_type);
|
|
TABLE_BOTTOM;
|
|
|
|
TABLE_TOP("Last Modified:");
|
|
if(cache_obj->last_modified)
|
|
{
|
|
PUT_PART(ctime(&cache_obj->last_modified));
|
|
}
|
|
else
|
|
{
|
|
PL_strcpy(buffer, "No date sent");
|
|
PUT_PART(buffer);
|
|
}
|
|
TABLE_BOTTOM;
|
|
|
|
TABLE_TOP("Expires:");
|
|
if(cache_obj->expires)
|
|
{
|
|
PUT_PART(ctime(&cache_obj->expires));
|
|
}
|
|
else
|
|
{
|
|
PL_strcpy(buffer, "No expiration date sent");
|
|
PUT_PART(buffer);
|
|
}
|
|
TABLE_BOTTOM;
|
|
|
|
TABLE_TOP("Last accessed:");
|
|
PUT_PART(ctime(&cache_obj->last_accessed));
|
|
TABLE_BOTTOM;
|
|
|
|
TABLE_TOP("Character set:");
|
|
if(cache_obj->charset)
|
|
{
|
|
PUT_PART(cache_obj->charset);
|
|
}
|
|
else
|
|
{
|
|
PL_strcpy(buffer, "iso-8859-1 (default)");
|
|
PUT_PART(buffer);
|
|
}
|
|
TABLE_BOTTOM;
|
|
|
|
HG27328
|
|
|
|
PL_strcpy(buffer, "\n<P>\n");
|
|
PUT_PART(buffer);
|
|
|
|
|
|
}
|
|
|
|
END:
|
|
PR_Free(buffer);
|
|
if(cur_entry->status < 0)
|
|
(*stream->abort)(stream, cur_entry->status);
|
|
else
|
|
(*stream->complete)(stream);
|
|
|
|
return;
|
|
}
|
|
|
|
/*Accessor for use by Cache Browser */
|
|
PUBLIC net_CacheObject*
|
|
NET_FirstMemCacheObject(XP_List* list_ptr)
|
|
{
|
|
if (net_MemoryCacheList)
|
|
{
|
|
list_ptr = net_MemoryCacheList;
|
|
return NET_NextMemCacheObject(list_ptr);
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*Accessor for use by Cache Browser */
|
|
PUBLIC net_CacheObject*
|
|
NET_NextMemCacheObject(XP_List* list_ptr)
|
|
{
|
|
if (list_ptr)
|
|
{
|
|
net_MemoryCacheObject * tmp = (net_MemoryCacheObject *) XP_ListNextObject(list_ptr);
|
|
if (tmp)
|
|
{
|
|
return &tmp->cache_obj;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#include "libmocha.h"
|
|
|
|
NET_StreamClass *
|
|
net_CloneWysiwygMemCacheEntry(MWContext *window_id, URL_Struct *URL_s,
|
|
uint32 nbytes, const char * wysiwyg_url,
|
|
const char * base_href)
|
|
#ifdef NU_CACHE
|
|
{
|
|
PR_ASSERT(0);
|
|
return NULL;
|
|
}
|
|
#else
|
|
{
|
|
net_MemoryCacheObject *memory_copy;
|
|
PRCList *link;
|
|
CacheDataObject *data_object;
|
|
NET_StreamClass *stream;
|
|
XP_List *list;
|
|
net_MemorySegment *seg;
|
|
uint32 len;
|
|
|
|
if (!(memory_copy = URL_s->memory_copy))
|
|
{
|
|
/* not hitting the cache -- check whether we're filling it */
|
|
for (link = mem_active_cache_data_objects.next;
|
|
link != &mem_active_cache_data_objects;
|
|
link = link->next)
|
|
{
|
|
data_object = (CacheDataObject *)link;
|
|
if (data_object->URL_s == URL_s &&
|
|
(memory_copy = data_object->memory_copy) != NULL)
|
|
{
|
|
goto found;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
found:
|
|
stream = LM_WysiwygCacheConverter(window_id, URL_s,
|
|
wysiwyg_url, base_href);
|
|
if (!stream)
|
|
return 0;
|
|
list = memory_copy->list;
|
|
while (nbytes != 0 &&
|
|
(seg = (net_MemorySegment *) XP_ListNextObject(list)) != NULL)
|
|
{
|
|
len = seg->seg_size;
|
|
if (len > nbytes)
|
|
len = nbytes;
|
|
if (stream->put_block(stream, seg->segment,
|
|
(int32)len) < 0)
|
|
break;
|
|
nbytes -= len;
|
|
}
|
|
if (nbytes != 0)
|
|
{
|
|
/* NB: Our caller must clear top_state->mocha_write_stream. */
|
|
stream->abort(stream, MK_UNABLE_TO_CONVERT);
|
|
PR_Free(stream);
|
|
return 0;
|
|
}
|
|
return stream;
|
|
}
|
|
#endif
|
|
|
|
PRIVATE void
|
|
net_CleanupMemoryCacheProtocol(void)
|
|
{
|
|
}
|
|
|
|
void
|
|
NET_InitMemCacProtocol(void) /* no prototype when NU_CACHE */
|
|
{
|
|
static NET_ProtoImpl mem_cac_proto_impl;
|
|
#ifdef NU_CACHE
|
|
PR_ASSERT(0);
|
|
return;
|
|
#endif
|
|
mem_cac_proto_impl.init = net_MemoryCacheLoad;
|
|
mem_cac_proto_impl.process = net_ProcessMemoryCache;
|
|
mem_cac_proto_impl.interrupt = net_InterruptMemoryCache;
|
|
mem_cac_proto_impl.cleanup = net_CleanupMemoryCacheProtocol;
|
|
|
|
NET_RegisterProtocolImplementation(&mem_cac_proto_impl, MEMORY_CACHE_TYPE_URL);
|
|
}
|
|
|
|
#ifdef NU_CACHE
|
|
typedef struct _NuCacheConData {
|
|
void* cache_object;
|
|
NET_StreamClass *stream;
|
|
} NuCacheConData;
|
|
|
|
#define FIRST_BUFF_SIZE 1024
|
|
|
|
PRIVATE int32
|
|
net_NuCacheLoad (ActiveEntry * cur_entry)
|
|
{
|
|
if (cur_entry && cur_entry->URL_s)
|
|
{
|
|
NuCacheConData* con_data = PR_NEW(NuCacheConData);
|
|
void* pObject = cur_entry->URL_s->cache_object;
|
|
|
|
if (!con_data || !pObject)
|
|
{
|
|
cur_entry->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
|
|
cur_entry->status = MK_OUT_OF_MEMORY;
|
|
return cur_entry->status;
|
|
}
|
|
|
|
cur_entry->protocol = NU_CACHE_TYPE_URL;
|
|
cur_entry->memory_file = (CacheObject_GetModule(pObject) == 0); /* TODO replace 0 with the enum */
|
|
|
|
/* CacheObject->SetReadLock(pObject); */
|
|
cur_entry->local_file = TRUE; /* Check about this one- TODO */
|
|
cur_entry->con_data = con_data;
|
|
cur_entry->socket = NULL;
|
|
|
|
NET_SetCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
|
|
FE_EnableClicking(cur_entry->window_id);
|
|
|
|
if (0==cur_entry->format_out)
|
|
{
|
|
/* This means that this is a CACHE_ONLY request and shouldn't
|
|
* be getting read from memory anyway. So mark it as not present in memory
|
|
* and return as such. */
|
|
cur_entry->status = MK_OBJECT_NOT_IN_CACHE;
|
|
return cur_entry->status;
|
|
}
|
|
|
|
/* Build the stream to read data from */
|
|
con_data->stream = NET_StreamBuilder(cur_entry->format_out, cur_entry->URL_s, cur_entry->window_id);
|
|
if(!con_data->stream)
|
|
{
|
|
NET_ClearCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
PR_DELETE(con_data);
|
|
cur_entry->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
|
|
cur_entry->status = MK_UNABLE_TO_CONVERT;
|
|
return cur_entry->status;
|
|
}
|
|
|
|
if (!cur_entry->URL_s->load_background)
|
|
FE_GraphProgressInit(cur_entry->window_id, cur_entry->URL_s, cur_entry->URL_s->content_length);
|
|
|
|
/* Process the first chunk so that images can start loading */
|
|
if (!CacheObject_IsPartial(pObject)) /*todo- change this to is Completed */
|
|
{
|
|
char* firstBuffer = (char*) PR_Malloc(FIRST_BUFF_SIZE);
|
|
PRInt32 amountRead = 0;
|
|
if (!firstBuffer)
|
|
{
|
|
PR_DELETE(con_data);
|
|
cur_entry->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
|
|
amountRead = CacheObject_Read(pObject, firstBuffer, FIRST_BUFF_SIZE);
|
|
if (amountRead > 0)
|
|
{
|
|
cur_entry->status = (*con_data->stream->put_block)(con_data->stream, firstBuffer, amountRead);
|
|
if(cur_entry->status < 0)
|
|
{
|
|
NET_ClearCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
|
|
if (!cur_entry->URL_s->load_background)
|
|
{
|
|
FE_GraphProgressDestroy(cur_entry->window_id,
|
|
cur_entry->URL_s,
|
|
cur_entry->URL_s->content_length,
|
|
cur_entry->bytes_received);
|
|
}
|
|
PR_DELETE(con_data);
|
|
return (cur_entry->status);
|
|
}
|
|
|
|
cur_entry->bytes_received += amountRead;
|
|
PR_DELETE(firstBuffer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cur_entry->status = 0;
|
|
}
|
|
}
|
|
return cur_entry->status;
|
|
}
|
|
|
|
/* called repeatedly from NET_ProcessNet to push all the
|
|
* data up the stream
|
|
*/
|
|
PRIVATE int32
|
|
net_ProcessNuCache (ActiveEntry * cur_entry)
|
|
{
|
|
if (cur_entry)
|
|
{
|
|
PRUint32 amountRead;
|
|
PRUint32 buffer_size;
|
|
|
|
NuCacheConData* con_data = (NuCacheConData*) cur_entry->con_data;
|
|
void* pObject = cur_entry->URL_s->cache_object;
|
|
|
|
NET_SetCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
|
|
if (!pObject)
|
|
{
|
|
cur_entry->status = MK_OBJECT_NOT_IN_CACHE;
|
|
return cur_entry->status;
|
|
}
|
|
|
|
/* Wait for the object to complete */
|
|
if (!CacheObject_GetIsCompleted(pObject))
|
|
return 0;
|
|
#if 0
|
|
/* If the object has been aborted or expired */
|
|
if ((CACHEOBJECT_ABORTED == CacheObject_GetState(pObject)) ||
|
|
(CACHEOBJECT_EXPIRED == CacheObject_GetState(pObject)))
|
|
{
|
|
(*con_data->stream->complete)(con_data->stream);
|
|
PR_Free(con_data->stream);
|
|
/* CacheObject->UnsetReadLock(pObject); */
|
|
PR_Free(con_data);
|
|
|
|
cur_entry->status = MK_DATA_LOADED;
|
|
NET_ClearCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
if (!cur_entry->URL_s->load_background)
|
|
FE_GraphProgressDestroy(
|
|
cur_entry->window_id,
|
|
cur_entry->URL_s,
|
|
cur_entry->URL_s->content_length,
|
|
cur_entry->bytes_received);
|
|
return -1;
|
|
}
|
|
#endif
|
|
/* Get the buffer size that the stream is ready to receive */
|
|
buffer_size = (*con_data->stream->is_write_ready)(con_data->stream);
|
|
|
|
/* if the stream doesn't want any data, continue */
|
|
if (0 == buffer_size)
|
|
{
|
|
cur_entry->status = 1;
|
|
return cur_entry->status;
|
|
}
|
|
|
|
/* Use the minimum from these factors.
|
|
* Not sure why we are doing this part. Will look into this later- Gagan
|
|
*/
|
|
buffer_size = MIN(buffer_size, (unsigned int) NET_Socket_Buffer_Size);
|
|
buffer_size = MIN(buffer_size, NET_MEM_CACHE_OUTPUT_SIZE);
|
|
|
|
amountRead = CacheObject_Read(pObject, NET_Socket_Buffer, buffer_size);
|
|
if (amountRead > 0)
|
|
{
|
|
cur_entry->status = (*con_data->stream->put_block)(con_data->stream, NET_Socket_Buffer, amountRead);
|
|
|
|
/* Update the progress control */
|
|
if (!cur_entry->URL_s->load_background)
|
|
FE_GraphProgress(
|
|
cur_entry->window_id,
|
|
cur_entry->URL_s,
|
|
cur_entry->bytes_received,
|
|
amountRead,
|
|
cur_entry->URL_s->content_length);
|
|
|
|
if(cur_entry->status < 0)
|
|
{
|
|
NET_ClearCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
|
|
if (!cur_entry->URL_s->load_background)
|
|
{
|
|
FE_GraphProgressDestroy(cur_entry->window_id,
|
|
cur_entry->URL_s,
|
|
cur_entry->URL_s->content_length,
|
|
cur_entry->bytes_received);
|
|
}
|
|
PR_Free(con_data);
|
|
return (cur_entry->status);
|
|
}
|
|
|
|
cur_entry->bytes_received += amountRead;
|
|
}
|
|
else /* we didn't read anything, must be the end. */
|
|
{
|
|
(*con_data->stream->complete)(con_data->stream);
|
|
PR_Free(con_data->stream);
|
|
/* Reset read locks, reset streams, etc. */
|
|
CacheObject_Reset(pObject);
|
|
|
|
PR_Free(con_data);
|
|
|
|
cur_entry->status = MK_DATA_LOADED;
|
|
NET_ClearCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
if (!cur_entry->URL_s->load_background)
|
|
FE_GraphProgressDestroy(
|
|
cur_entry->window_id,
|
|
cur_entry->URL_s,
|
|
cur_entry->URL_s->content_length,
|
|
cur_entry->bytes_received);
|
|
return -1;
|
|
}
|
|
|
|
return cur_entry->status;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* called by functions in mkgeturl to interrupt the loading of
|
|
* an object. (Usually a user interrupt)
|
|
*/
|
|
PRIVATE int32
|
|
net_InterruptNuCache (ActiveEntry * cur_entry)
|
|
{
|
|
if (cur_entry)
|
|
{
|
|
NuCacheConData* con_data = (NuCacheConData*) cur_entry->con_data;
|
|
void* pObject = cur_entry->URL_s->cache_object;
|
|
|
|
(*con_data->stream->abort)(con_data->stream, MK_INTERRUPTED);
|
|
PR_Free(con_data->stream);
|
|
/* Reset read locks, reset streams, etc. */
|
|
CacheObject_Reset(pObject);
|
|
|
|
PR_Free(con_data);
|
|
|
|
NET_ClearCallNetlibAllTheTime(cur_entry->window_id, "nucache");
|
|
|
|
cur_entry->status = MK_INTERRUPTED;
|
|
|
|
return cur_entry->status;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
PRIVATE void
|
|
net_CleanupNuCacheProtocol(void)
|
|
{
|
|
}
|
|
|
|
void
|
|
NET_InitNuCacheProtocol(void)
|
|
{
|
|
static NET_ProtoImpl nu_cache_proto_impl;
|
|
|
|
nu_cache_proto_impl.init = net_NuCacheLoad;
|
|
nu_cache_proto_impl.process = net_ProcessNuCache;
|
|
nu_cache_proto_impl.interrupt = net_InterruptNuCache;
|
|
nu_cache_proto_impl.cleanup = net_CleanupNuCacheProtocol;
|
|
|
|
NET_RegisterProtocolImplementation(&nu_cache_proto_impl, NU_CACHE_TYPE_URL);
|
|
}
|
|
#endif /* NU_CACHE */
|
|
#endif /* MOZILLA_CLIENT */
|