PassRefPtr<Blob> Blob::slice(long long start, long long length) const { // When we slice a file for the first time, we obtain a snapshot of the file by capturing its current size and modification time. // The modification time will be used to verify if the file has been changed or not, when the underlying data are accessed. long long snapshotSize; double snapshotModificationTime; if (m_snapshotCaptured) { snapshotSize = m_snapshotSize; snapshotModificationTime = m_snapshotModificationTime; } else { // If we fail to retrieve the size or modification time, probably due to that the file has been deleted, an empty blob will be returned. time_t modificationTime; if (!getFileSize(m_path, snapshotSize) || !getFileModificationTime(m_path, modificationTime)) { snapshotSize = 0; snapshotModificationTime = 0; } else snapshotModificationTime = modificationTime; } // Clamp the range if it exceeds the size limit. if (start < 0) start = 0; if (length < 0) length = 0; if (start > snapshotSize) { start = 0; length = 0; } else if (start + length > snapshotSize) length = snapshotSize - start; return adoptRef(new Blob(m_path, m_start + start, length, snapshotSize, snapshotModificationTime)); }
static void removeAllDatabasesForOriginPath(const String& originPath, std::chrono::system_clock::time_point modifiedSince) { Vector<String> databasePaths = listDirectory(originPath, "*"); for (auto& databasePath : databasePaths) { String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3"); if (!fileExists(databaseFile)) continue; if (modifiedSince > std::chrono::system_clock::time_point::min()) { time_t modificationTime; if (!getFileModificationTime(databaseFile, modificationTime)) continue; if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince) continue; } SQLiteFileSystem::deleteDatabaseFile(databaseFile); deleteEmptyDirectory(databasePath); } deleteEmptyDirectory(originPath); }
static void removeAllDatabasesForOriginPath(const String& originPath, double startDate, double endDate) { // FIXME: We should also close/invalidate any live handles to the database files we are about to delete. // Right now: // - For read-only operations, they will continue functioning as normal on the unlinked file. // - For write operations, they will start producing errors as SQLite notices the missing backing store. // This is tracked by https://bugs.webkit.org/show_bug.cgi?id=135347 Vector<String> databasePaths = listDirectory(originPath, "*"); for (auto& databasePath : databasePaths) { String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3"); if (!fileExists(databaseFile)) continue; time_t modTime; getFileModificationTime(databaseFile, modTime); if (modTime < startDate || modTime > endDate) continue; deleteFile(databaseFile); deleteEmptyDirectory(databasePath); } deleteEmptyDirectory(originPath); }
void DatabaseTracker::deleteDatabasesModifiedSince(std::chrono::system_clock::time_point time) { Vector<RefPtr<SecurityOrigin>> originsCopy; origins(originsCopy); for (auto& origin : originsCopy) { Vector<String> databaseNames; if (!databaseNamesForOrigin(origin.get(), databaseNames)) continue; size_t deletedDatabases = 0; for (auto& databaseName : databaseNames) { auto fullPath = fullPathForDatabase(origin.get(), databaseName, false); time_t modificationTime; if (!getFileModificationTime(fullPath, modificationTime)) continue; if (modificationTime < std::chrono::system_clock::to_time_t(time)) continue; deleteDatabase(origin.get(), databaseName); ++deletedDatabases; } if (deletedDatabases == databaseNames.size()) deleteOrigin(origin.get()); } }
void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const { unsigned long flashPlayerPluginPathUTF8ByteLen = 0; unsigned char* const flashPlayerPluginPathUTF8Bytes = WebKitApollo::g_HostFunctions->getFlashPlayerPluginPathUTF8Bytes(&flashPlayerPluginPathUTF8ByteLen); ASSERT(flashPlayerPluginPathUTF8Bytes); ASSERT(flashPlayerPluginPathUTF8ByteLen > 0); String flashPlayerPluginPath(UTF8Encoding().decode(reinterpret_cast<const char*>(flashPlayerPluginPathUTF8Bytes), flashPlayerPluginPathUTF8ByteLen)); WebKitApollo::g_HostFunctions->freeBytes(flashPlayerPluginPathUTF8Bytes); time_t lastModified; if (getFileModificationTime(flashPlayerPluginPath, lastModified)) { paths.add(flashPlayerPluginPath); } // Adobe Reader WebString* pdfPluginPath = WebKitApollo::g_HostFunctions->getPDFPluginPathWebString(); if(pdfPluginPath) { paths.add(adoptWebString(pdfPluginPath)); } }
static Optional<time_t> fileModificationTime(const String& filePath) { time_t time; if (!getFileModificationTime(filePath, time)) return Nullopt; return time; }
double File::lastModifiedDate() const { time_t modificationTime; if (getFileModificationTime(m_path, modificationTime) && isValidFileTime(modificationTime)) return modificationTime * msPerSecond; return currentTime() * msPerSecond; }
double File::lastModifiedDate() const { time_t modificationTime; if (!getFileModificationTime(m_path, modificationTime)) return 0; // Needs to return epoch time in milliseconds for Date. return modificationTime * 1000.0; }
void FileStream::openForRead(Blob* blob) { ASSERT(!isMainThread()); if (isHandleValid(m_handle)) return; // FIXME: Need to handle multiple items that may include non-file ones when BlobBuilder is introduced. ASSERT(blob->items().size() >= 1); const FileBlobItem* fileItem = blob->items().at(0)->toFileBlobItem(); if (!fileItem) { ASSERT(false); m_client->didFail(NOT_READABLE_ERR); return; } // Check if the file exists by querying its modification time. We choose not to call fileExists() in order to save an // extra file system call when the modification time is needed to check the validity of the sliced file blob. // Per the spec, we need to return different error codes to differentiate between non-existent file and permission error. // openFile() could not tell use the failure reason. time_t currentModificationTime; if (!getFileModificationTime(fileItem->path(), currentModificationTime)) { m_client->didFail(NOT_FOUND_ERR); return; } // Open the file blob. m_handle = openFile(fileItem->path(), OpenForRead); if (!isHandleValid(m_handle)) { m_client->didFail(NOT_READABLE_ERR); return; } #if ENABLE(BLOB_SLICE) const FileRangeBlobItem* fileRangeItem = fileItem->toFileRangeBlobItem(); if (fileRangeItem) { // Check the modificationt time for the possible file change. if (static_cast<time_t>(fileRangeItem->snapshotModificationTime()) != currentModificationTime) { m_client->didFail(NOT_READABLE_ERR); return; } // Jump to the beginning position if the file has been sliced. if (fileRangeItem->start() > 0) { if (seekFile(m_handle, fileRangeItem->start(), SeekFromBeginning) < 0) { m_client->didFail(NOT_READABLE_ERR); return; } } } #endif // Get the size. m_totalBytesToRead = blob->size(); m_client->didGetSize(m_totalBytesToRead); }
double File::lastModifiedMS() const { if (hasValidSnapshotMetadata() && isValidFileTime(m_snapshotModificationTimeMS)) return m_snapshotModificationTimeMS; double modificationTimeMS; if (hasBackingFile() && getFileModificationTime(m_path, modificationTimeMS) && isValidFileTime(modificationTimeMS)) return modificationTimeMS; return currentTimeMS(); }
void File::captureSnapshot(long long& snapshotSize, double& snapshotModificationTime) const { // Obtains a snapshot of the file by capturing its current size and modification time. This is used when we slice a file for the first time. // If we fail to retrieve the size or modification time, probably due to that the file has been deleted, 0 size is returned. // FIXME: Combine getFileSize and getFileModificationTime into one file system call. time_t modificationTime; if (!getFileSize(m_path, snapshotSize) || !getFileModificationTime(m_path, modificationTime)) { snapshotSize = 0; snapshotModificationTime = 0; } else snapshotModificationTime = modificationTime; }
// Return false if we cannot advance the stream. Currently the only possible failure is that the underlying file has been removed or changed since File.slice. static bool advanceCurrentStream(FormStreamFields* form) { closeCurrentStream(form); if (form->remainingElements.isEmpty()) return true; // Create the new stream. FormDataElement& nextInput = form->remainingElements.last(); if (nextInput.m_type == FormDataElement::data) { size_t size = nextInput.m_data.size(); MallocPtr<char> data = nextInput.m_data.releaseBuffer(); form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data.get()), size, kCFAllocatorNull); form->currentData = std::move(data); } else { #if ENABLE(BLOB) // Check if the file has been changed or not if required. if (isValidFileTime(nextInput.m_expectedFileModificationTime)) { time_t fileModificationTime; if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime)) return false; } #endif const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename; form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get()); if (!form->currentStream) { // The file must have been removed or become unreadable. return false; } #if ENABLE(BLOB) if (nextInput.m_fileStart > 0) { RetainPtr<CFNumberRef> position = adoptCF(CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart)); CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position.get()); } form->currentStreamRangeLength = nextInput.m_fileLength; #endif } form->remainingElements.removeLast(); // Set up the callback. CFStreamClientContext context = { 0, form, 0, 0, 0 }; CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, formEventCallback, &context); // Schedule with the current set of run loops. SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end(); for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it) CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode()); return true; }
double File::lastModifiedDate() const { #if ENABLE(FILE_SYSTEM) if (hasValidSnapshotMetadata() && isValidFileTime(m_snapshotModificationTime)) return m_snapshotModificationTime * msPerSecond; #endif time_t modificationTime; if (getFileModificationTime(m_path, modificationTime) && isValidFileTime(modificationTime)) return modificationTime * msPerSecond; return currentTime() * msPerSecond; }
double File::lastModifiedDate() const { #if ENABLE(FILE_SYSTEM) if (m_snapshotSize >= 0 && m_snapshotModificationTime) return m_snapshotModificationTime * 1000.0; #endif time_t modificationTime; if (!getFileModificationTime(m_path, modificationTime)) return 0; // Needs to return epoch time in milliseconds for Date. return modificationTime * 1000.0; }
double File::lastModifiedDate() const { #if ENABLE(FILE_SYSTEM) if (hasValidSnapshotMetadata()) return m_snapshotModificationTime * 1000.0; #endif time_t modificationTime; if (!getFileModificationTime(m_path, modificationTime)) return invalidFileTime(); // Needs to return epoch time in milliseconds for Date. return modificationTime * 1000.0; }
long long FileStream::getSize(const String& path, double expectedModificationTime) { // Check the modification time for the possible file change. time_t modificationTime; if (!getFileModificationTime(path, modificationTime)) return -1; if (expectedModificationTime) { if (static_cast<time_t>(expectedModificationTime) != modificationTime) return -1; } // Now get the file size. long long length; if (!getFileSize(path, length)) return -1; return length; }
const String& Page::userStyleSheet() const { if (m_userStyleSheetPath.isEmpty()) { ASSERT(m_userStyleSheet.isEmpty()); return m_userStyleSheet; } time_t modTime; if (!getFileModificationTime(m_userStyleSheetPath, modTime)) { // The stylesheet either doesn't exist, was just deleted, or is // otherwise unreadable. If we've read the stylesheet before, we should // throw away that data now as it no longer represents what's on disk. m_userStyleSheet = String(); return m_userStyleSheet; } // If the stylesheet hasn't changed since the last time we read it, we can // just return the old data. if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime) return m_userStyleSheet; m_didLoadUserStyleSheet = true; m_userStyleSheet = String(); m_userStyleSheetModificationTime = modTime; // FIXME: It would be better to load this asynchronously to avoid blocking // the process, but we will first need to create an asynchronous loading // mechanism that is not tied to a particular Frame. We will also have to // determine what our behavior should be before the stylesheet is loaded // and what should happen when it finishes loading, especially with respect // to when the load event fires, when Document::close is called, and when // layout/paint are allowed to happen. RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath); if (!data) return m_userStyleSheet; m_userStyleSheet = TextResourceDecoder::create("text/css")->decode(data->data(), data->size()); return m_userStyleSheet; }
void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const { unsigned long flashPlayerPluginPathUTF8ByteLen = 0; unsigned char* const flashPlayerPluginPathUTF8Bytes = WebKitApollo::g_HostFunctions->getFlashPlayerPluginPathUTF8Bytes(&flashPlayerPluginPathUTF8ByteLen); ASSERT(flashPlayerPluginPathUTF8Bytes); ASSERT(flashPlayerPluginPathUTF8ByteLen > 0); String flashPlayerPluginPath(UTF8Encoding().decode(reinterpret_cast<const char*>(flashPlayerPluginPathUTF8Bytes), flashPlayerPluginPathUTF8ByteLen)); WebKitApollo::g_HostFunctions->freeBytes(flashPlayerPluginPathUTF8Bytes); time_t lastModified; if (getFileModificationTime(flashPlayerPluginPath, lastModified)) { #if PLATFORM(APOLLO) && !CPU(X86_64) // loading the flashplayer plugin is not supported yet on Win64 paths.add(flashPlayerPluginPath); #endif } WebString* pdfPluginPath = WebKitApollo::g_HostFunctions->getPDFPluginPathWebString(); if(pdfPluginPath) { paths.add(adoptWebString(pdfPluginPath)); } }
bool PluginDatabase::refresh() { #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) if (!m_persistentMetadataCacheIsLoaded) loadPersistentMetadataCache(); #endif bool pluginSetChanged = false; if (!m_plugins.isEmpty()) { PluginSet pluginsToUnload; getDeletedPlugins(pluginsToUnload); // Unload plugins PluginSet::const_iterator end = pluginsToUnload.end(); for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it) remove(it->get()); pluginSetChanged = !pluginsToUnload.isEmpty(); } HashSet<String> paths; getPluginPathsInDirectories(paths); HashMap<String, time_t> pathsWithTimes; // We should only skip unchanged files if we didn't remove any plugins above. If we did remove // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions // of RealPlayer installed and just removed the newer one, we'll pick up the older one. bool shouldSkipUnchangedFiles = !pluginSetChanged; HashSet<String>::const_iterator pathsEnd = paths.end(); for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) { time_t lastModified; if (!getFileModificationTime(*it, lastModified)) continue; pathsWithTimes.add(*it, lastModified); // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything. if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified) continue; if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) { ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified); remove(oldPackage.get()); } RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified); if (package && add(package.release())) pluginSetChanged = true; } // Cache all the paths we found with their timestamps for next time. pathsWithTimes.swap(m_pluginPathsWithTimes); if (!pluginSetChanged) return false; #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) updatePersistentMetadataCache(); #endif m_registeredMIMETypes.clear(); // Register plug-in MIME types PluginSet::const_iterator end = m_plugins.end(); for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { // Get MIME types MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin(); MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end(); for (; map_it != map_end; ++map_it) m_registeredMIMETypes.add(map_it->first); } return true; }
void* workerThread(void* args) { int client_sd = *((int*)args); int tid = pthread_self(); //debug printf("%sAccepted client_sd: %d, tid: %d%s\n", BG_GREEN, client_sd, tid, DEFAULT); // Flags for the loop int server_sd; int server_sd_reusable = 0; int server_response_has_no_body; int is_case_4; int file_already_cached; while(1) { // Initialize flags server_response_has_no_body = 0; is_case_4 = 0; file_already_cached = 0; // Read HTTP Request from the client char clientRequest[MAX_REQUEST_SIZE]; strcpy(clientRequest, ""); int bytesReceived = recv(client_sd, clientRequest, sizeof(clientRequest), 0); if(bytesReceived < 0) { printf("%sreceive error: %s (Errno:%d)%s\n",BG_RED, strerror(errno), errno, DEFAULT); pthread_exit(0); } else if (bytesReceived == 0) { printf("%stid %d: Client closed the connection. %s\n", BG_RED, tid, DEFAULT); pthread_exit(0); } //debug printf("%s=== clientRequest ===%s\n", BG_YELLOW, DEFAULT); printf("/%s%s%s/\n",BG_BLUE, clientRequest, DEFAULT); printf("%s=== End of clientRequest ===%s\n", BG_YELLOW, DEFAULT); // Parse the HTTP Request to struct requestAttributes struct requestAttributes clientRequestAttributes = parseRequestMessage(clientRequest); printf("%sGOOGLE ANALYTICS%s\n", BG_RED, DEFAULT); // If server_sd can be reused, reuse it // Else, establish a connection to server if (server_sd_reusable) printf("Server sd is reusable. Reuse server_sd: /%s%d%s/ now. \n", BG_PURPLE, server_sd, DEFAULT); else server_sd = connectToServer(clientRequestAttributes.Host, clientRequestAttributes.port); server_sd_reusable = 1; char buf[MAX_REQUEST_SIZE]; strcpy(buf, clientRequest); //debug printRequestAttributes(&clientRequestAttributes); // If the method is not GET, simply ignore it if (clientRequestAttributes.methodNotGET) { printf("%sIgnored a non-GET request.%s\n", BG_PURPLE, DEFAULT); continue; } // If the file type is not the ones that need caching, // simply forwards the request to the web server if (!clientRequestAttributes.typeNeedsCaching) { printf("%sThe request file type does not need caching. Simply forward the request to server now.%s\n", BG_PURPLE, DEFAULT); int byteSent = send(server_sd, buf, strlen(buf), MSG_NOSIGNAL); printf("Sent /%s%d%s/ bytes to the server. \n", BG_PURPLE, byteSent, DEFAULT); if (byteSent < 0) { printf("%sForward HTTP Request Error%s\n", BG_RED, DEFAULT); continue; } } else { // If the file is not cached in MYPROXY, // simply forwards it to the web server if (getFileModificationTime(clientRequestAttributes.URL) == -1) { printf("%sThe file is not cached on the proxy. Simply forward the request to server now. %s\n", BG_PURPLE, DEFAULT); int byteSent = send(server_sd, buf, strlen(buf), MSG_NOSIGNAL); if (byteSent < 0) { printf("%sForward HTTP Request Error%s\n", BG_RED, DEFAULT); continue; } } else { file_already_cached = 1; // Here goes the four cases int caseNumber; if (clientRequestAttributes.noCache) { if (clientRequestAttributes.IMS == 0) caseNumber = 3; else caseNumber = 4; } else { if (clientRequestAttributes.IMS == 0) caseNumber = 1; else caseNumber = 2; } //debug printf("%scaseNumber is: %d%s\n", BG_PURPLE, caseNumber, DEFAULT); // didn't use switch since I fear to mess up // the break in switch with the break in while if (caseNumber == 1) { // Case 1: // Proxy directly respond the client with the cache if (respondCache(clientRequestAttributes.URL, client_sd) == -1) break; goto oneLoopisDone; } if (caseNumber == 2) { // Case 2: // If IMS is later than LMT of the cached web object, // just respond 304 // else, respond the client with 200 and the web object if (clientRequestAttributes.IMS > getFileModificationTime(clientRequestAttributes.URL)) { char block[512]; sprintf(block, "HTTP/1.1 304 Not Modified\r\n\r\n"); if (send(client_sd, block, strlen(block), MSG_NOSIGNAL) <= 0) break; server_response_has_no_body = 1; } else if (respondCache(clientRequestAttributes.URL, client_sd) == -1) break; goto oneLoopisDone; } if (caseNumber == 3) { // Case 3: // Add If-Modified-Since: *LMT* header Line to the client Request // and then forward it to server time_t LMT = getFileModificationTime(clientRequestAttributes.URL); char timeString[50]; getTimeStringfromRawTime(timeString, LMT); char* headerEnding = strstr(buf, "\r\n\r\n"); *headerEnding = '\0'; sprintf(headerEnding, "\r\nIf-Modified-Since: %s\r\n\r\n", timeString); //debug printf("%s=== case 3 buf ===%s\n", BG_YELLOW, DEFAULT); printf("/%s%s%s/\n", BG_BLUE, buf, DEFAULT); printf("%s=== End of case 3 buf ===%s\n", BG_YELLOW, DEFAULT); int byteSent = send(server_sd, buf, strlen(buf), MSG_NOSIGNAL); if (byteSent < 0) { printf("%sForward HTTP Request Error%s\n", BG_RED, DEFAULT); continue; } } if (caseNumber == 4) { // Case 4: // If IMS is later than LMT of the cached web object, // simply forwards it to the web server // Else, edit If-Modified-Since: ... header Line of the client request // edit the date to LMT // and then forward it to the web server is_case_4 = 1; time_t LMT = getFileModificationTime(clientRequestAttributes.URL); if (clientRequestAttributes.IMS < LMT) { char* pointerToIMS = strstr(buf, "If-Modified-Since"); char* pointerToLatterPart = strstr(pointerToIMS, "\r\n"); char latterPart[MAX_REQUEST_SIZE]; strcpy(latterPart, pointerToLatterPart); pointerToIMS+=19; // Maybe problematic? *pointerToIMS = '\0'; char timeString[50]; getTimeStringfromRawTime(timeString, LMT); strcat(buf, timeString); strcat(buf, latterPart); //debug printf("%s=== case 4 buf ===%s\n", BG_YELLOW, DEFAULT); printf("/%s%s%s/\n", BG_BLUE, buf, DEFAULT); printf("%s=== End of case 4 buf ===%s\n", BG_YELLOW, DEFAULT); } int byteSent = send(server_sd, buf, strlen(buf), MSG_NOSIGNAL); if (byteSent < 0) { printf("%sForward HTTP Request Error%s\n", BG_RED, DEFAULT); continue; } } } } // Now receive server's HTTP Response's header char receiveBuffer[MAX_RESPONSE_SIZE]; memset(receiveBuffer, 0, sizeof(receiveBuffer)); char* receiveBufferPtr = receiveBuffer; printf("%sWaiting for the server's response...%s\n", BG_PURPLE, DEFAULT); // Receive byte by byte until an \r\n\r\n is found while(1) { bytesReceived = recv(server_sd, receiveBufferPtr, 1, 0); if (bytesReceived < 0) { printf("%sreceive HTTP response error%s\n", BG_RED, DEFAULT); break; } //debug else if (bytesReceived == 0) { printf("%sServer Closed the connection%s\n", BG_RED, DEFAULT); break; // Maybe problematic } else { //printf("%sReceived one byte%s ", BG_PURPLE, DEFAULT); if (strstr(receiveBuffer, "\r\n\r\n") != NULL) break; receiveBufferPtr++; } } receiveBufferPtr++; *receiveBufferPtr = '\0'; //debug printf("\n%s=== server's response's header ===%s\n", BG_YELLOW, DEFAULT); printf("/%s%s%s/\n", BG_BLUE, receiveBuffer, DEFAULT); printf("%s=== End of server's response's header ===%s\n", BG_YELLOW, DEFAULT); // parse server's response's header struct responseAttributes serverResponseAttributes = parseResponseHeader(receiveBuffer); printResponseAttributes(&serverResponseAttributes); if (serverResponseAttributes.contentLength == 0 && serverResponseAttributes.isChunked == 0) server_response_has_no_body = 1; // 200 case if (serverResponseAttributes.statusCode == 200) { // If the file type needs caching, then cache it if (clientRequestAttributes.typeNeedsCaching) { if (cacheServerResponse(receiveBuffer, clientRequestAttributes.URL, server_sd, client_sd, server_response_has_no_body) == -1) { printf("%scache Error%s\n", BG_RED, DEFAULT); break; } } // Else, simply forward the response else { if (forwardServerResponse(receiveBuffer, server_sd, client_sd, server_response_has_no_body) == -1) { printf("%sforward Error%s\n", BG_RED, DEFAULT); break; } } printf("%sok here?2%s\n", FG_RED, DEFAULT); } // 304 case else if (serverResponseAttributes.statusCode == 304) { if (is_case_4) { if (respondCache(clientRequestAttributes.URL, client_sd) == -1) { printf("%srespond Error%s\n", BG_RED, DEFAULT); break; } goto forwardBackComplete; } else { if (forwardServerResponse(receiveBuffer, server_sd, client_sd, server_response_has_no_body) == -1) { printf("%sforward Error%s\n", BG_RED, DEFAULT); break; } } } // Status code is otherwise case else { if (forwardServerResponse(receiveBuffer, server_sd, client_sd, server_response_has_no_body) == -1) { printf("%sforward Error%s\n", BG_RED, DEFAULT); break; } } forwardBackComplete: oneLoopisDone: if (serverResponseAttributes.serverClose) { printf("%sThe server's response has Connection: close. Close server_sd now. %s\n", BG_PURPLE, DEFAULT); server_sd_reusable = 0; close(server_sd); } // This part is a workaround for the mysterious phenomenon // that the server_sd, after a c->p->s->p->c loop, cannot be reused // even though server did not respond "Connection: close" or likewise // Here call recv() once to test if server closed the connection at its side // if server did, close the server_sd char testByte; bytesReceived = recv(server_sd, &testByte, 1, MSG_PEEK | MSG_DONTWAIT); if (bytesReceived == 0) { printf("%sThe server silently closed the Connection. Close server_sd now. %s\n", BG_PURPLE, DEFAULT); server_sd_reusable = 0; close(server_sd); } if (server_response_has_no_body) break; if (clientRequestAttributes.clientClose) break; printf("%sOne Loop(c->p->s->p->c) is done.%s\n", BG_PURPLE, DEFAULT); } close(server_sd); close(client_sd); pthread_exit(0); }
double SQLiteFileSystem::databaseModificationTime(const String& fileName) { time_t time; return getFileModificationTime(fileName, time) ? time : 0; }
bool CurlCacheEntry::parseResponseHeaders(ResourceResponse& response) { double fileTime; time_t fileModificationDate; if (getFileModificationTime(m_headerFilename, fileModificationDate)) { fileTime = difftime(fileModificationDate, 0); fileTime *= 1000.0; } else fileTime = currentTimeMS(); // GMT if (response.cacheControlContainsNoCache() || response.cacheControlContainsNoStore()) return false; double maxAge = 0; bool maxAgeIsValid = false; if (response.cacheControlContainsMustRevalidate()) maxAge = 0; else { maxAge = response.cacheControlMaxAge(); if (std::isnan(maxAge)) maxAge = 0; else maxAgeIsValid = true; } if (!response.hasCacheValidatorFields()) return false; double lastModificationDate = 0; double responseDate = 0; double expirationDate = 0; lastModificationDate = response.lastModified(); if (std::isnan(lastModificationDate)) lastModificationDate = 0; responseDate = response.date(); if (std::isnan(responseDate)) responseDate = 0; expirationDate = response.expires(); if (std::isnan(expirationDate)) expirationDate = 0; if (maxAgeIsValid) { // when both the cache entry and the response contain max-age, the lesser one takes priority double expires = fileTime + maxAge * 1000; if (m_expireDate == -1 || m_expireDate > expires) m_expireDate = expires; } else if (responseDate > 0 && expirationDate >= responseDate) m_expireDate = fileTime + (expirationDate - responseDate); // if there were no lifetime information if (m_expireDate == -1) { if (lastModificationDate > 0) m_expireDate = fileTime + (fileTime - lastModificationDate) * 0.1; else m_expireDate = 0; } String etag = response.httpHeaderField("ETag"); if (!etag.isNull()) m_requestHeaders.set("If-None-Match", etag); String lastModified = response.httpHeaderField("Last-Modified"); if (!lastModified.isNull()) m_requestHeaders.set("If-Modified-Since", lastModified); if (etag.isNull() && lastModified.isNull()) return false; m_headerInMemory = true; return true; }