📄 formdatastreammac.mm
字号:
/* * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *//* originally written by Becky Willrich, additional code by Darin Adler */#import "config.h"#import "FormDataStreamMac.h"#import "CString.h"#import "FormData.h"#import "ResourceHandle.h"#import "ResourceHandleClient.h"#import "SchedulePair.h"#import "WebCoreSystemInterface.h"#import <sys/stat.h>#import <sys/types.h>#import <wtf/Assertions.h>#import <wtf/HashMap.h>#import <wtf/MainThread.h>#import <wtf/StdLibExtras.h>#import <wtf/Threading.h>namespace WebCore {typedef HashMap<CFReadStreamRef, RefPtr<FormData> > StreamFormDataMap;static StreamFormDataMap& getStreamFormDataMap(){ DEFINE_STATIC_LOCAL(StreamFormDataMap, streamFormDataMap, ()); return streamFormDataMap;}typedef HashMap<CFReadStreamRef, RefPtr<ResourceHandle> > StreamResourceHandleMap;static StreamResourceHandleMap& getStreamResourceHandleMap(){ DEFINE_STATIC_LOCAL(StreamResourceHandleMap, streamResourceHandleMap, ()); return streamResourceHandleMap;}void associateStreamWithResourceHandle(NSInputStream *stream, ResourceHandle* resourceHandle){ ASSERT(isMainThread()); ASSERT(resourceHandle); if (!stream) return; if (!getStreamFormDataMap().contains((CFReadStreamRef)stream)) return; ASSERT(!getStreamResourceHandleMap().contains((CFReadStreamRef)stream)); getStreamResourceHandleMap().set((CFReadStreamRef)stream, resourceHandle);}void disassociateStreamWithResourceHandle(NSInputStream *stream){ ASSERT(isMainThread()); if (!stream) return; getStreamResourceHandleMap().remove((CFReadStreamRef)stream);}struct DidSendDataCallbackData { DidSendDataCallbackData(CFReadStreamRef stream_, unsigned long long bytesSent_, unsigned long long streamLength_) : stream(stream_) , bytesSent(bytesSent_) , streamLength(streamLength_) { } CFReadStreamRef stream; unsigned long long bytesSent; unsigned long long streamLength;};static void performDidSendDataCallback(void* context){ ASSERT(isMainThread()); DidSendDataCallbackData* data = static_cast<DidSendDataCallbackData*>(context); ResourceHandle* resourceHandle = getStreamResourceHandleMap().get(data->stream).get(); if (resourceHandle && resourceHandle->client()) resourceHandle->client()->didSendData(resourceHandle, data->bytesSent, data->streamLength); delete data;}static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);struct FormContext { FormData* formData; unsigned long long streamLength;};struct FormStreamFields { SchedulePairHashSet scheduledRunLoopPairs; Vector<FormDataElement> remainingElements; // in reverse order CFReadStreamRef currentStream; char* currentData; CFReadStreamRef formStream; unsigned long long streamLength; unsigned long long bytesSent;};static void closeCurrentStream(FormStreamFields *form){ if (form->currentStream) { CFReadStreamClose(form->currentStream); CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL); CFRelease(form->currentStream); form->currentStream = NULL; } if (form->currentData) { fastFree(form->currentData); form->currentData = 0; }}static void advanceCurrentStream(FormStreamFields *form){ closeCurrentStream(form); if (form->remainingElements.isEmpty()) return; // Create the new stream. FormDataElement& nextInput = form->remainingElements.last(); if (nextInput.m_type == FormDataElement::data) { size_t size = nextInput.m_data.size(); char* data = nextInput.m_data.releaseBuffer(); form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull); form->currentData = data; } else { const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename; CFStringRef filename = path.createCFString(); CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE); CFRelease(filename); form->currentStream = CFReadStreamCreateWithFile(0, fileURL); CFRelease(fileURL); } form->remainingElements.removeLast(); // Set up the callback. CFStreamClientContext context = { 0, form, NULL, NULL, NULL }; 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());}static void openNextStream(FormStreamFields* form){ // Skip over any streams we can't open. // For some purposes we might want to return an error, but the current NSURLConnection // can't really do anything useful with an error at this point, so this is better. advanceCurrentStream(form); while (form->currentStream && !CFReadStreamOpen(form->currentStream)) advanceCurrentStream(form);}static void* formCreate(CFReadStreamRef stream, void* context){ FormContext* formContext = static_cast<FormContext*>(context); FormStreamFields* newInfo = new FormStreamFields; newInfo->currentStream = NULL; newInfo->currentData = 0; newInfo->formStream = stream; // Don't retain. That would create a reference cycle. newInfo->streamLength = formContext->streamLength; newInfo->bytesSent = 0; FormData* formData = formContext->formData; // Append in reverse order since we remove elements from the end. size_t size = formData->elements().size(); newInfo->remainingElements.reserveInitialCapacity(size); for (size_t i = 0; i < size; ++i) newInfo->remainingElements.append(formData->elements()[size - i - 1]); getStreamFormDataMap().set(stream, adoptRef(formData)); return newInfo;}static void formFinalize(CFReadStreamRef stream, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); getStreamFormDataMap().remove(stream); closeCurrentStream(form); delete form;}static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); openNextStream(form); *openComplete = TRUE; error->error = 0; return TRUE;}static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); while (form->currentStream) { CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength); if (bytesRead < 0) { *error = CFReadStreamGetError(form->currentStream); return -1; } if (bytesRead > 0) { error->error = 0; *atEOF = FALSE; form->bytesSent += bytesRead; if (!ResourceHandle::didSendBodyDataDelegateExists()) { // FIXME: Figure out how to only do this when a ResourceHandleClient is available. DidSendDataCallbackData* data = new DidSendDataCallbackData(stream, form->bytesSent, form->streamLength); callOnMainThread(performDidSendDataCallback, data); } return bytesRead; } openNextStream(form); } error->error = 0; *atEOF = TRUE; return 0;}static Boolean formCanRead(CFReadStreamRef stream, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) { openNextStream(form); } if (!form->currentStream) { wkSignalCFReadStreamEnd(stream); return FALSE; } return CFReadStreamHasBytesAvailable(form->currentStream);}static void formClose(CFReadStreamRef, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); closeCurrentStream(form);}static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); if (form->currentStream) CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode); form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));}static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); if (form->currentStream) CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode); form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));}static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context){ FormStreamFields* form = static_cast<FormStreamFields*>(context); switch (type) { case kCFStreamEventHasBytesAvailable: wkSignalCFReadStreamHasBytes(form->formStream); break; case kCFStreamEventErrorOccurred: { CFStreamError readStreamError = CFReadStreamGetError(stream); wkSignalCFReadStreamError(form->formStream, &readStreamError); break; } case kCFStreamEventEndEncountered: openNextStream(form); if (!form->currentStream) { wkSignalCFReadStreamEnd(form->formStream); } break; case kCFStreamEventNone: LOG_ERROR("unexpected kCFStreamEventNone"); break; case kCFStreamEventOpenCompleted: LOG_ERROR("unexpected kCFStreamEventOpenCompleted"); break; case kCFStreamEventCanAcceptBytes: LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes"); break; }}void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData){ if (!formData) return; size_t count = formData->elements().size(); // Handle the common special case of one piece of form data, with no files. if (count == 1 && !formData->alwaysStream()) { const FormDataElement& element = formData->elements()[0]; if (element.m_type == FormDataElement::data) { NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()]; [request setHTTPBody:data]; [data release]; return; } } // Precompute the content length so NSURLConnection doesn't use chunked mode. long long length = 0; for (size_t i = 0; i < count; ++i) { const FormDataElement& element = formData->elements()[i]; if (element.m_type == FormDataElement::data) length += element.m_data.size(); else { const String& filename = element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename; struct stat sb; int statResult = stat(filename.utf8().data(), &sb); if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG) length += sb.st_size; } } // Set the length. [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"]; // Create and set the stream. // Pass the length along with the formData so it does not have to be recomputed. FormContext formContext = { formData.releaseRef(), length }; CFReadStreamRef stream = wkCreateCustomCFReadStream(formCreate, formFinalize, formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule, &formContext); [request setHTTPBodyStream:(NSInputStream *)stream]; CFRelease(stream);}FormData* httpBodyFromStream(NSInputStream* stream){ return getStreamFormDataMap().get((CFReadStreamRef)stream).get();}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -