📄 resourcehandlesoup.cpp
字号:
/* * Copyright (C) 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2008 Xan Lopez <xan@gnome.org> * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2009 Holger Hans Peter Freyther * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> * Copyright (C) 2009 Christian Dywan <christian@imendio.com> * Copyright (C) 2009 Igalia S.L. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */#include "config.h"#include "ResourceHandle.h"#include "Base64.h"#include "CookieJarSoup.h"#include "ChromeClient.h"#include "CString.h"#include "DocLoader.h"#include "Frame.h"#include "HTTPParsers.h"#include "Logging.h"#include "MIMETypeRegistry.h"#include "NotImplemented.h"#include "Page.h"#include "ResourceError.h"#include "ResourceHandleClient.h"#include "ResourceHandleInternal.h"#include "ResourceResponse.h"#include "TextEncoding.h"#include <errno.h>#include <fcntl.h>#include <gio/gio.h>#include <gtk/gtk.h>#include <libsoup/soup.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#if PLATFORM(GTK)#define USE_GLIB_BASE64#endifnamespace WebCore {class WebCoreSynchronousLoader : public ResourceHandleClient, Noncopyable {public: WebCoreSynchronousLoader(ResourceError&, ResourceResponse &, Vector<char>&); ~WebCoreSynchronousLoader(); virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); virtual void didFinishLoading(ResourceHandle*); virtual void didFail(ResourceHandle*, const ResourceError&); void run();private: ResourceError& m_error; ResourceResponse& m_response; Vector<char>& m_data; bool m_finished; GMainLoop* m_mainLoop;};WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) : m_error(error) , m_response(response) , m_data(data) , m_finished(false){ m_mainLoop = g_main_loop_new(0, false);}WebCoreSynchronousLoader::~WebCoreSynchronousLoader(){ g_main_loop_unref(m_mainLoop);}void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response){ m_response = response;}void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int){ m_data.append(data, length);}void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*){ g_main_loop_quit(m_mainLoop); m_finished = true;}void WebCoreSynchronousLoader::didFail(ResourceHandle* handle, const ResourceError& error){ m_error = error; didFinishLoading(handle);}void WebCoreSynchronousLoader::run(){ if (!m_finished) g_main_loop_run(m_mainLoop);}enum{ ERROR_TRANSPORT, ERROR_UNKNOWN_PROTOCOL, ERROR_BAD_NON_HTTP_METHOD, ERROR_UNABLE_TO_OPEN_FILE,};struct FileMapping{ gpointer ptr; gsize length;};static void freeFileMapping(gpointer data){ FileMapping* fileMapping = static_cast<FileMapping*>(data); if (fileMapping->ptr != MAP_FAILED) munmap(fileMapping->ptr, fileMapping->length); g_slice_free(FileMapping, fileMapping);}static void cleanupGioOperation(ResourceHandleInternal* handle);ResourceHandleInternal::~ResourceHandleInternal(){ if (m_msg) { g_object_unref(m_msg); m_msg = 0; } cleanupGioOperation(this); if (m_idleHandler) { g_source_remove(m_idleHandler); m_idleHandler = 0; }}ResourceHandle::~ResourceHandle(){}static void fillResponseFromMessage(SoupMessage* msg, ResourceResponse* response){ SoupMessageHeadersIter iter; const char* name = 0; const char* value = 0; soup_message_headers_iter_init(&iter, msg->response_headers); while (soup_message_headers_iter_next(&iter, &name, &value)) response->setHTTPHeaderField(name, value); String contentType = soup_message_headers_get(msg->response_headers, "Content-Type"); char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); response->setUrl(KURL(uri)); g_free(uri); response->setMimeType(extractMIMETypeFromMediaType(contentType)); response->setTextEncodingName(extractCharsetFromMediaType(contentType)); response->setExpectedContentLength(soup_message_headers_get_content_length(msg->response_headers)); response->setHTTPStatusCode(msg->status_code); response->setHTTPStatusText(msg->reason_phrase); response->setSuggestedFilename(filenameFromHTTPContentDisposition(response->httpHeaderField("Content-Disposition")));}// Called each time the message is going to be sent again except the first time.// It's used mostly to let webkit know about redirects.static void restartedCallback(SoupMessage* msg, gpointer data){ ResourceHandle* handle = static_cast<ResourceHandle*>(data); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); if (d->m_cancelled) return; char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); String location = String(uri); g_free(uri); KURL newURL = KURL(handle->request().url(), location); ResourceRequest request = handle->request(); ResourceResponse response; request.setURL(newURL); fillResponseFromMessage(msg, &response); if (d->client()) d->client()->willSendRequest(handle, request, response); d->m_request.setURL(newURL);}static void gotHeadersCallback(SoupMessage* msg, gpointer data){ if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) return; // We still don't know anything about Content-Type, so we will try // sniffing the contents of the file, and then report that we got // headers if (!soup_message_headers_get_content_type(msg->response_headers, NULL)) return; ResourceHandle* handle = static_cast<ResourceHandle*>(data); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); if (d->m_cancelled) return; ResourceHandleClient* client = handle->client(); if (!client) return; fillResponseFromMessage(msg, &d->m_response); client->didReceiveResponse(handle, d->m_response); d->m_reportedHeaders = true;}static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data){ if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) return; ResourceHandle* handle = static_cast<ResourceHandle*>(data); if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); if (d->m_cancelled) return; ResourceHandleClient* client = handle->client(); if (!client) return; if (!d->m_reportedHeaders) { gboolean uncertain; char* contentType = g_content_type_guess(d->m_request.url().lastPathComponent().utf8().data(), reinterpret_cast<const guchar*>(chunk->data), chunk->length, &uncertain); soup_message_headers_set_content_type(msg->response_headers, contentType, NULL); g_free(contentType); fillResponseFromMessage(msg, &d->m_response); client->didReceiveResponse(handle, d->m_response); d->m_reportedHeaders = true; } client->didReceiveData(handle, chunk->data, chunk->length, false);}// Called at the end of the message, with all the necessary about the last informations.// Doesn't get called for redirects.static void finishedCallback(SoupSession *session, SoupMessage* msg, gpointer data){ RefPtr<ResourceHandle>handle = adoptRef(static_cast<ResourceHandle*>(data)); // TODO: maybe we should run this code even if there's no client? if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (!client) return; if (d->m_cancelled) return; if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); ResourceError error("webkit-network-error", ERROR_TRANSPORT, uri, String::fromUTF8(msg->reason_phrase)); g_free(uri); client->didFail(handle.get(), error); return; } if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { fillResponseFromMessage(msg, &d->m_response); client->didReceiveResponse(handle.get(), d->m_response); // WebCore might have cancelled the job in the while if (d->m_cancelled) return; if (msg->response_body->data) client->didReceiveData(handle.get(), msg->response_body->data, msg->response_body->length, true); } client->didFinishLoading(handle.get());}// parseDataUrl() is taken from the CURL http backend.static gboolean parseDataUrl(gpointer callback_data){ ResourceHandle* handle = static_cast<ResourceHandle*>(callback_data); ResourceHandleClient* client = handle->client(); handle->getInternal()->m_idleHandler = 0; ASSERT(client); if (!client) return false; String url = handle->request().url().string(); ASSERT(url.startsWith("data:", false)); int index = url.find(','); if (index == -1) { client->cannotShowURL(handle); return false; } String mediaType = url.substring(5, index - 5); String data = url.substring(index + 1); bool isBase64 = mediaType.endsWith(";base64", false); if (isBase64) mediaType = mediaType.left(mediaType.length() - 7); if (mediaType.isEmpty()) mediaType = "text/plain;charset=US-ASCII"; String mimeType = extractMIMETypeFromMediaType(mediaType); String charset = extractCharsetFromMediaType(mediaType); ResourceResponse response; response.setMimeType(mimeType); if (isBase64) { data = decodeURLEscapeSequences(data); response.setTextEncodingName(charset); client->didReceiveResponse(handle, response); // Use the GLib Base64 if available, since WebCore's decoder isn't // general-purpose and fails on Acid3 test 97 (whitespace).#ifdef USE_GLIB_BASE64 size_t outLength = 0; char* outData = 0; outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength)); if (outData && outLength > 0) client->didReceiveData(handle, outData, outLength, 0); g_free(outData);#else Vector<char> out; if (base64Decode(data.latin1().data(), data.latin1().length(), out) && out.size() > 0) client->didReceiveData(handle, out.data(), out.size(), 0);#endif } else { // We have to convert to UTF-16 early due to limitations in KURL data = decodeURLEscapeSequences(data, TextEncoding(charset)); response.setTextEncodingName("UTF-16"); client->didReceiveResponse(handle, response); if (data.length() > 0) client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0); } client->didFinishLoading(handle); return false;}bool ResourceHandle::startData(String urlString){ ResourceHandleInternal* d = this->getInternal(); // If parseDataUrl is called synchronously the job is not yet effectively started // and webkit won't never know that the data has been parsed even didFinishLoading is called. d->m_idleHandler = g_idle_add(parseDataUrl, this); return true;}static SoupSession* createSoupSession(){ return soup_session_async_new();}static void ensureSessionIsInitialized(SoupSession* session){ if (g_object_get_data(G_OBJECT(session), "webkit-init")) return; SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); if (!jar) soup_session_add_feature(session, SOUP_SESSION_FEATURE(defaultCookieJar())); else setDefaultCookieJar(jar); if (!soup_session_get_feature(session, SOUP_TYPE_LOGGER) && LogNetwork.state == WTFLogChannelOn) { SoupLogger* logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(SOUP_LOGGER_LOG_BODY), -1); soup_logger_attach(logger, session); g_object_unref(logger); } g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef));}bool ResourceHandle::startHttp(String urlString){ SoupSession* session = defaultSession(); ensureSessionIsInitialized(session); SoupMessage* msg; msg = soup_message_new(request().httpMethod().utf8().data(), urlString.utf8().data()); g_signal_connect(msg, "restarted", G_CALLBACK(restartedCallback), this); g_signal_connect(msg, "got-headers", G_CALLBACK(gotHeadersCallback), this); g_signal_connect(msg, "got-chunk", G_CALLBACK(gotChunkCallback), this);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -