📄 resourcehandlesoup.cpp
字号:
g_object_set_data(G_OBJECT(msg), "resourceHandle", reinterpret_cast<void*>(this)); HTTPHeaderMap customHeaders = d->m_request.httpHeaderFields(); if (!customHeaders.isEmpty()) { HTTPHeaderMap::const_iterator end = customHeaders.end(); for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) soup_message_headers_append(msg->request_headers, it->first.string().utf8().data(), it->second.utf8().data()); } FormData* httpBody = d->m_request.httpBody(); if (httpBody && !httpBody->isEmpty()) { size_t numElements = httpBody->elements().size(); // handle the most common case (i.e. no file upload) if (numElements < 2) { Vector<char> body; httpBody->flatten(body); soup_message_set_request(msg, d->m_request.httpContentType().utf8().data(), SOUP_MEMORY_COPY, body.data(), body.size()); } else { /* * we have more than one element to upload, and some may * be (big) files, which we will want to mmap instead of * copying into memory; TODO: support upload of non-local * (think sftp://) files by using GIO? */ soup_message_body_set_accumulate(msg->request_body, FALSE); for (size_t i = 0; i < numElements; i++) { const FormDataElement& element = httpBody->elements()[i]; if (element.m_type == FormDataElement::data) soup_message_body_append(msg->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size()); else { /* * mapping for uploaded files code inspired by technique used in * libsoup's simple-httpd test */ /* FIXME: Since Linux 2.6.23 we should also use O_CLOEXEC */ int fd = open(element.m_filename.utf8().data(), O_RDONLY); if (fd == -1) { ResourceError error("webkit-network-error", ERROR_UNABLE_TO_OPEN_FILE, urlString, strerror(errno)); d->client()->didFail(this, error); g_object_unref(msg); return false; } struct stat statBuf; fstat(fd, &statBuf); FileMapping* fileMapping = g_slice_new(FileMapping); fileMapping->ptr = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (fileMapping->ptr == MAP_FAILED) { ResourceError error("webkit-network-error", ERROR_UNABLE_TO_OPEN_FILE, urlString, strerror(errno)); d->client()->didFail(this, error); freeFileMapping(fileMapping); g_object_unref(msg); close(fd); return false; } fileMapping->length = statBuf.st_size; close(fd); SoupBuffer* soupBuffer = soup_buffer_new_with_owner(fileMapping->ptr, fileMapping->length, fileMapping, freeFileMapping); soup_message_body_append_buffer(msg->request_body, soupBuffer); soup_buffer_free(soupBuffer); } } } } d->m_msg = static_cast<SoupMessage*>(g_object_ref(msg)); // balanced by a deref() in finishedCallback, which should always run ref(); // We handle each chunk ourselves, and we don't need msg->response_body // to contain all of the data we got, when we finish downloading. soup_message_body_set_accumulate(msg->response_body, FALSE); soup_session_queue_message(session, d->m_msg, finishedCallback, this); return true;}static gboolean reportUnknownProtocolError(gpointer callback_data){ ResourceHandle* handle = static_cast<ResourceHandle*>(callback_data); ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (d->m_cancelled || !client) { handle->deref(); return false; } KURL url = handle->request().url(); ResourceError error("webkit-network-error", ERROR_UNKNOWN_PROTOCOL, url.string(), url.protocol()); client->didFail(handle, error); handle->deref(); return false;}bool ResourceHandle::start(Frame* frame){ ASSERT(!d->m_msg); // The frame could be null if the ResourceHandle is not associated to any // Frame, e.g. if we are downloading a file. // If the frame is not null but the page is null this must be an attempted // load from an onUnload handler, so let's just block it. if (frame && !frame->page()) return false; KURL url = request().url(); String urlString = url.string(); String protocol = url.protocol(); // Used to set the authentication dialog toplevel; may be NULL d->m_frame = frame; if (equalIgnoringCase(protocol, "data")) return startData(urlString); if ((equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https")) && SOUP_URI_VALID_FOR_HTTP(soup_uri_new(urlString.utf8().data()))) return startHttp(urlString); if (equalIgnoringCase(protocol, "file") || equalIgnoringCase(protocol, "ftp") || equalIgnoringCase(protocol, "ftps")) // FIXME: should we be doing any other protocols here? return startGio(url); // Error must not be reported immediately, but through an idle function. // Despite error, we should return true so a proper handle is created, // to which this failure can be reported. ref(); d->m_idleHandler = g_idle_add(reportUnknownProtocolError, this); return true;}void ResourceHandle::cancel(){ d->m_cancelled = true; if (d->m_msg) soup_session_cancel_message(defaultSession(), d->m_msg, SOUP_STATUS_CANCELLED); else if (d->m_cancellable) g_cancellable_cancel(d->m_cancellable);}PassRefPtr<SharedBuffer> ResourceHandle::bufferedData(){ ASSERT_NOT_REACHED(); return 0;}bool ResourceHandle::supportsBufferedData(){ return false;}void ResourceHandle::setDefersLoading(bool defers){ d->m_defersLoading = defers; notImplemented();}bool ResourceHandle::loadsBlocked(){ return false;}bool ResourceHandle::willLoadFromCache(ResourceRequest&){ // Not having this function means that we'll ask the user about re-posting a form // even when we go back to a page that's still in the cache. notImplemented(); return false;}void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data, Frame* frame){ WebCoreSynchronousLoader syncLoader(error, response, data); ResourceHandle handle(request, &syncLoader, true, false, true); handle.start(frame); syncLoader.run();}// GIO-based loaderstatic inline ResourceError networkErrorForFile(GFile* file, GError* error){ // FIXME: Map gio errors to a more detailed error code when we have it in WebKit. gchar* uri = g_file_get_uri(file); ResourceError resourceError("webkit-network-error", ERROR_TRANSPORT, uri, error ? String::fromUTF8(error->message) : String()); g_free(uri); return resourceError;}static void cleanupGioOperation(ResourceHandleInternal* d){ if (d->m_gfile) { g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", 0); g_object_unref(d->m_gfile); d->m_gfile = 0; } if (d->m_cancellable) { g_object_unref(d->m_cancellable); d->m_cancellable = 0; } if (d->m_inputStream) { g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", 0); g_object_unref(d->m_inputStream); d->m_inputStream = 0; } if (d->m_buffer) { g_free(d->m_buffer); d->m_buffer = 0; }}static void closeCallback(GObject* source, GAsyncResult* res, gpointer){ ResourceHandle* handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); g_input_stream_close_finish(d->m_inputStream, res, 0); cleanupGioOperation(d); client->didFinishLoading(handle);}static void readCallback(GObject* source, GAsyncResult* res, gpointer){ // didReceiveData may cancel the load, which may release the last reference. RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (d->m_cancelled || !client) { cleanupGioOperation(d); return; } GError *error = 0; gssize bytesRead = g_input_stream_read_finish(d->m_inputStream, res, &error); if (error) { ResourceError resourceError = networkErrorForFile(d->m_gfile, error); g_error_free(error); cleanupGioOperation(d); client->didFail(handle.get(), resourceError); return; } if (!bytesRead) { g_input_stream_close_async(d->m_inputStream, G_PRIORITY_DEFAULT, 0, closeCallback, 0); return; } d->m_total += bytesRead; client->didReceiveData(handle.get(), d->m_buffer, bytesRead, d->m_total); if (d->m_cancelled) { cleanupGioOperation(d); return; } g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize, G_PRIORITY_DEFAULT, d->m_cancellable, readCallback, 0);}static void openCallback(GObject* source, GAsyncResult* res, gpointer){ ResourceHandle* handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (d->m_cancelled || !client) { cleanupGioOperation(d); return; } GError *error = 0; GFileInputStream* in = g_file_read_finish(G_FILE(source), res, &error); if (error) { ResourceError resourceError = networkErrorForFile(d->m_gfile, error); g_error_free(error); cleanupGioOperation(d); client->didFail(handle, resourceError); return; } d->m_inputStream = G_INPUT_STREAM(in); d->m_bufferSize = 8192; d->m_buffer = static_cast<char*>(g_malloc(d->m_bufferSize)); d->m_total = 0; g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", handle); g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize, G_PRIORITY_DEFAULT, d->m_cancellable, readCallback, 0);}static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer){ ResourceHandle* handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (d->m_cancelled) { cleanupGioOperation(d); return; } ResourceResponse response; char* uri = g_file_get_uri(d->m_gfile); response.setUrl(KURL(uri)); g_free(uri); GError *error = 0; GFileInfo* info = g_file_query_info_finish(d->m_gfile, res, &error); if (error) { // FIXME: to be able to handle ftp URIs properly, we must // check if the error is G_IO_ERROR_NOT_MOUNTED, and if so, // call g_file_mount_enclosing_volume() to mount the ftp // server (and then keep track of the fact that we mounted it, // and set a timeout to unmount it later after it's been idle // for a while). ResourceError resourceError = networkErrorForFile(d->m_gfile, error); g_error_free(error); cleanupGioOperation(d); client->didFail(handle, resourceError); return; } if (g_file_info_get_file_type(info) != G_FILE_TYPE_REGULAR) { // FIXME: what if the URI points to a directory? Should we // generate a listing? How? What do other backends do here? ResourceError resourceError = networkErrorForFile(d->m_gfile, 0); cleanupGioOperation(d); client->didFail(handle, resourceError); return; } response.setMimeType(g_file_info_get_content_type(info)); response.setExpectedContentLength(g_file_info_get_size(info)); GTimeVal tv; g_file_info_get_modification_time(info, &tv); response.setLastModifiedDate(tv.tv_sec); client->didReceiveResponse(handle, response); g_file_read_async(d->m_gfile, G_PRIORITY_DEFAULT, d->m_cancellable, openCallback, 0);}bool ResourceHandle::startGio(KURL url){ if (request().httpMethod() != "GET" && request().httpMethod() != "POST") { ResourceError error("webkit-network-error", ERROR_BAD_NON_HTTP_METHOD, url.string(), request().httpMethod()); d->client()->didFail(this, error); return false; } // GIO doesn't know how to handle refs and queries, so remove them // TODO: use KURL.fileSystemPath after KURLGtk and FileSystemGtk are // using GIO internally, and providing URIs instead of file paths url.removeRef(); url.setQuery(String()); url.setPort(0); // we avoid the escaping for local files, because // g_filename_from_uri (used internally by GFile) has problems // decoding strings with arbitrary percent signs if (url.isLocalFile()) d->m_gfile = g_file_new_for_path(url.prettyURL().utf8().data() + sizeof("file://") - 1); else d->m_gfile = g_file_new_for_uri(url.string().utf8().data()); g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", this); d->m_cancellable = g_cancellable_new(); g_file_query_info_async(d->m_gfile, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, d->m_cancellable, queryInfoCallback, 0); return true;}SoupSession* ResourceHandle::defaultSession(){ static SoupSession* session = createSoupSession();; return session;}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -