void MyResourceLoader::loadData(const String& data) { LOGD("Loading data (%s) ...", data.latin1().data()); ResourceHandleClient* client = m_handle->client(); int index = data.find(','); if (index == -1) { client->cannotShowURL(m_handle); return; } String mediaType = data.substring(0, index); String base64 = data.substring(index + 1); bool decode = mediaType.endsWith(";base64", false); if (decode) mediaType = mediaType.left(mediaType.length() - 7); // 7 for base64; if (mediaType.isEmpty()) mediaType = "text/plain;charset=US-ASCII"; String mimeType = extractMIMETypeFromMediaType(mediaType); String charset = extractCharsetFromMediaType(mediaType); ResourceResponse response; response.setMimeType(mimeType); if (decode) { base64 = decodeURLEscapeSequences(base64); response.setTextEncodingName(charset); client->didReceiveResponse(m_handle, response); // FIXME: This is annoying. WebCore's Base64 decoder chokes on spaces. // That is correct with strict decoding but html authors (particularly // the acid3 authors) put spaces in the data which should be ignored. // Remove them here before sending to the decoder. Vector<char> in; CString str = base64.latin1(); const char* chars = str.data(); unsigned i = 0; while (i < str.length()) { char c = chars[i]; // Don't send spaces or control characters. if (c != ' ' && c != '\n' && c != '\t' && c != '\b' && c != '\f' && c != '\r') in.append(chars[i]); i++; } Vector<char> out; if (base64Decode(in, out) && out.size() > 0) client->didReceiveData(m_handle, out.data(), out.size(), 0); } else { base64 = decodeURLEscapeSequences(base64, TextEncoding(charset)); response.setTextEncodingName("UTF-16"); client->didReceiveResponse(m_handle, response); if (base64.length() > 0) client->didReceiveData(m_handle, (const char*)base64.characters(), base64.length() * sizeof(UChar), 0); } client->didFinishLoading(m_handle, 0); }
void QNetworkReplyHandler::sendResponseIfNeeded() { m_shouldSendResponse = (m_loadMode != LoadNormal); if (m_shouldSendResponse) return; if (m_reply->error()) return; if (m_responseSent || !m_resourceHandle) return; m_responseSent = true; ResourceHandleClient* client = m_resourceHandle->client(); if (!client) return; WebCore::String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString(); WebCore::String encoding = extractCharsetFromMediaType(contentType); WebCore::String mimeType = extractMIMETypeFromMediaType(contentType); if (mimeType.isEmpty()) { // let's try to guess from the extension QString extension = m_reply->url().path(); int index = extension.lastIndexOf(QLatin1Char('.')); if (index > 0) { extension = extension.mid(index + 1); mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension); } } KURL url(m_reply->url()); ResourceResponse response(url, mimeType, m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), encoding, String()); if (url.isLocalFile()) { client->didReceiveResponse(m_resourceHandle, response); return; } // The status code is equal to 0 for protocols not in the HTTP family. int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (url.protocolInHTTPFamily()) { String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->rawHeader("Content-Disposition"))); if (!suggestedFilename.isEmpty()) response.setSuggestedFilename(suggestedFilename); else response.setSuggestedFilename(url.lastPathComponent()); response.setHTTPStatusCode(statusCode); response.setHTTPStatusText(m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData()); // Add remaining headers. foreach (const QByteArray& headerName, m_reply->rawHeaderList()) { response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(m_reply->rawHeader(headerName))); } }
// This callback will not be called if the content sniffer is disabled in startHttp. static void contentSniffedCallback(SoupMessage* msg, const char* sniffedType, GHashTable *params, gpointer data) { if (sniffedType) { const char* officialType = soup_message_headers_get_one(msg->response_headers, "Content-Type"); if (!officialType || strcmp(officialType, sniffedType)) soup_message_headers_set_content_type(msg->response_headers, sniffedType, params); } if (statusWillBeHandledBySoup(msg->status_code)) return; RefPtr<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.get(), d->m_response); }
static gboolean parseDataUrl(gpointer callback_data) { ResourceHandle* handle = static_cast<ResourceHandle*>(callback_data); String data = handle->request().url().string(); ASSERT(data.startsWith("data:", false)); String header; bool base64 = false; int index = data.find(','); if (index != -1) { header = data.substring(5, index - 5).lower(); data = data.substring(index + 1); if (header.endsWith(";base64")) { base64 = true; header = header.left(header.length() - 7); } } else data = String(); data = decodeURLEscapeSequences(data); size_t outLength = 0; char* outData = 0; if (base64 && !data.isEmpty()) { // Use the GLib Base64 if available, since WebCore's decoder isn't // general-purpose and fails on Acid3 test 97 (whitespace). outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength)); } if (header.isEmpty()) header = "text/plain;charset=US-ASCII"; ResourceHandleClient* client = handle->getInternal()->client(); ResourceResponse response; response.setMimeType(extractMIMETypeFromMediaType(header)); response.setTextEncodingName(extractCharsetFromMediaType(header)); if (outData) response.setExpectedContentLength(outLength); else response.setExpectedContentLength(data.length()); response.setHTTPStatusCode(200); client->didReceiveResponse(handle, response); if (outData) client->didReceiveData(handle, outData, outLength, 0); else client->didReceiveData(handle, data.latin1().data(), data.length(), 0); g_free(outData); client->didFinishLoading(handle); return FALSE; }
static void gotHeadersCallback(SoupMessage* msg, gpointer data) { // For 401, we will accumulate the resource body, and only use it // in case authentication with the soup feature doesn't happen if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { soup_message_body_set_accumulate(msg->response_body, TRUE); return; } // For all the other responses, 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); RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); // The content-sniffed callback will handle the response if WebCore // require us to sniff. if(!handle || statusWillBeHandledBySoup(msg->status_code) || handle->shouldContentSniff()) 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.get(), d->m_response); }
static void contentSniffedCallback(SoupMessage* msg, const char* sniffedType, GHashTable *params, gpointer data) { if (sniffedType) { const char* officialType = soup_message_headers_get_one(msg->response_headers, "Content-Type"); if (!officialType || strcmp(officialType, sniffedType)) soup_message_headers_set_content_type(msg->response_headers, sniffedType, params); } // The 304 status code (SOUP_STATUS_NOT_MODIFIED) needs to be fed // into WebCore, as opposed to other kinds of redirections, which // are handled by soup directly, so we special-case it here and in // gotChunk. if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code) || (SOUP_STATUS_IS_REDIRECTION(msg->status_code) && (msg->status_code != SOUP_STATUS_NOT_MODIFIED)) || (msg->status_code == SOUP_STATUS_UNAUTHORIZED)) return; RefPtr<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.get(), d->m_response); }
void QNetworkReplyHandler::sendResponseIfNeeded() { ASSERT(m_replyWrapper && m_replyWrapper->reply() && !wasAborted()); if (m_replyWrapper->reply()->error() && m_replyWrapper->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).isNull()) return; ResourceHandleClient* client = m_resourceHandle->client(); if (!client) return; WTF::String mimeType = m_replyWrapper->mimeType(); if (mimeType.isEmpty()) { // let's try to guess from the extension mimeType = MIMETypeRegistry::getMIMETypeForPath(m_replyWrapper->reply()->url().path()); } KURL url(m_replyWrapper->reply()->url()); ResourceResponse response(url, mimeType.lower(), m_replyWrapper->reply()->header(QNetworkRequest::ContentLengthHeader).toLongLong(), m_replyWrapper->encoding(), String()); if (url.isLocalFile()) { client->didReceiveResponse(m_resourceHandle, response); return; } // The status code is equal to 0 for protocols not in the HTTP family. int statusCode = m_replyWrapper->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (url.protocolIsInHTTPFamily()) { String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromLatin1(m_replyWrapper->reply()->rawHeader("Content-Disposition"))); if (!suggestedFilename.isEmpty()) response.setSuggestedFilename(suggestedFilename); else { Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(mimeType); if (extensions.isEmpty()) response.setSuggestedFilename(url.lastPathComponent()); else { // If the suffix doesn't match the MIME type, correct the suffix. QString filename = url.lastPathComponent(); const String suffix = QMimeDatabase().suffixForFileName(filename); if (!extensions.contains(suffix)) { filename.chop(suffix.length()); filename += MIMETypeRegistry::getPreferredExtensionForMIMEType(mimeType); } response.setSuggestedFilename(filename); } } response.setHTTPStatusCode(statusCode); response.setHTTPStatusText(m_replyWrapper->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData()); // Add remaining headers. foreach (const QNetworkReply::RawHeaderPair& pair, m_replyWrapper->reply()->rawHeaderPairs()) response.setHTTPHeaderField(QString::fromLatin1(pair.first), QString::fromLatin1(pair.second)); }
void ResourceHandleInternal::didReceiveResponse(WebURLLoader*, const WebURLResponse& response) { ASSERT(m_client); ASSERT(!response.isNull()); bool isMultipart = response.isMultipartPayload(); bool isValidStateTransition = (m_state == ConnectionStateStarted || m_state == ConnectionStateReceivedResponse); // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved. if (!isMultipart && !isValidStateTransition) CRASH(); m_state = ConnectionStateReceivedResponse; m_client->didReceiveResponse(m_owner, response.toResourceResponse()); }
/* * This is being called for each HTTP header in the response. This includes '\r\n' * for the last line of the header. * * We will add each HTTP Header to the ResourceResponse and on the termination * of the header (\r\n) we will parse Content-Type and Content-Disposition and * update the ResourceResponse and then send it away. * */ static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data) { ResourceHandle* job = static_cast<ResourceHandle*>(data); ResourceHandleInternal* d = job->getInternal(); unsigned int totalSize = size * nmemb; ResourceHandleClient* client = d->client(); if (!client) { return totalSize; } String header(static_cast<const char*>(ptr), totalSize); /* * a) We can finish and send the ResourceResponse * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse */ if (header == String("\r\n")) { CURL* h = d->m_handle; CURLcode err; double contentLength = 0; err = curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength); d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength)); const char* hdr; err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr); d->m_response.setUrl(KURL(hdr)); long httpCode = 0; err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode); d->m_response.setHTTPStatusCode(httpCode); d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type"))); d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type"))); d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition"))); client->didReceiveResponse(job, d->m_response); } else { int splitPos = header.find(":"); if (splitPos != -1) d->m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace()); } return totalSize; }
static void gotHeadersCallback(SoupMessage* msg, 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; fillResponseFromMessage(msg, &d->m_response); client->didReceiveResponse(handle, d->m_response); soup_message_set_flags(msg, SOUP_MESSAGE_OVERWRITE_CHUNKS); }
// 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(g_quark_to_string(SOUP_HTTP_ERROR), msg->status_code, uri, String::fromUTF8(msg->reason_phrase)); g_free(uri); client->didFail(handle.get(), error); return; } if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { 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()); }
// 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) { ResourceHandle* handle = static_cast<ResourceHandle*>(data); // TODO: maybe we should run this code even if there's no client? if (!handle) return; ResourceHandleInternal* d = handle->getInternal(); // The message has been handled. d->m_msg = NULL; 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, error); return; } else if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { fillResponseFromMessage(msg, &d->m_response); client->didReceiveResponse(handle, 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, msg->response_body->data, msg->response_body->length, true); } client->didFinishLoading(handle); }
void MyResourceLoader::loadFile(const String& file) { LOGD("Loading file (%s) ...", file.latin1().data()); FILE* f = fopen(file.latin1().data(), "r"); ResourceHandleClient* client = m_handle->client(); if (!f) { client->didFail(m_handle, ResourceError("", -14, file, "Could not open file")); } else { ResourceResponse response; response.setTextEncodingName("utf-8"); response.setMimeType(mimeTypeForExtension(file)); client->didReceiveResponse(m_handle, response); char buf[512]; while (true) { int res = fread(buf, 1, sizeof(buf), f); if (res <= 0) break; client->didReceiveData(m_handle, buf, res, 0); } fclose(f); client->didFinishLoading(m_handle, 0); } }
/* * This is being called for each HTTP header in the response. This includes '\r\n' * for the last line of the header. * * We will add each HTTP Header to the ResourceResponse and on the termination * of the header (\r\n) we will parse Content-Type and Content-Disposition and * update the ResourceResponse and then send it away. * */ static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data) { ResourceHandle* job = static_cast<ResourceHandle*>(data); ResourceHandleInternal* d = job->getInternal(); if (d->m_cancelled) return 0; size_t totalSize = size * nmemb; ResourceHandleClient* client = d->client(); String header(static_cast<const char*>(ptr), totalSize); /* * a) We can finish and send the ResourceResponse * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse * * The HTTP standard requires to use \r\n but for compatibility it recommends to * accept also \n. */ if (header == String("\r\n") || header == String("\n")) { CURL* h = d->m_handle; CURLcode err; double contentLength = 0; err = curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength); d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength)); const char* hdr; err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr); d->m_response.setUrl(KURL(hdr)); long httpCode = 0; err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode); d->m_response.setHTTPStatusCode(httpCode); d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type"))); d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type"))); d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition"))); // HTTP redirection if (httpCode >= 300 && httpCode < 400) { String location = d->m_response.httpHeaderField("location"); if (!location.isEmpty()) { KURL newURL = KURL(job->request().url(), location); ResourceRequest redirectedRequest = job->request(); redirectedRequest.setURL(newURL); if (client) client->willSendRequest(job, redirectedRequest, d->m_response); d->m_request.setURL(newURL); return totalSize; } } if (client) client->didReceiveResponse(job, d->m_response); d->m_response.setResponseFired(true); } else { int splitPos = header.find(":"); if (splitPos != -1) d->m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace()); } return totalSize; }
// 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(); ResourceHandleInternal* d = handle->getInternal(); if (d->m_cancelled) return false; d->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); if (d->m_cancelled) return false; // Use the GLib Base64, since WebCore's decoder isn't // general-purpose and fails on Acid3 test 97 (whitespace). 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 { // 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 (d->m_cancelled) return false; if (data.length() > 0) client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0); if (d->m_cancelled) return false; } client->didFinishLoading(handle); return false; }
static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer data) { ResourceHandle* handle = static_cast<ResourceHandle*>(data); ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (d->m_cancelled) { cleanupGioOperation(handle); return; } ResourceResponse response; char* uri = g_file_get_uri(d->m_gfile); response.setUrl(KURL(uri)); g_free(uri); GError *error = NULL; 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). cleanupGioOperation(handle); if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND) response.setHTTPStatusCode(SOUP_STATUS_NOT_FOUND); else if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_PERMISSION_DENIED) response.setHTTPStatusCode(SOUP_STATUS_FORBIDDEN); else response.setHTTPStatusCode(SOUP_STATUS_BAD_REQUEST); // ? g_error_free(error); // FIXME: do we need to fake up a response body containing the // error message? client->didReceiveResponse(handle, response); client->didFinishLoading(handle); 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? cleanupGioOperation(handle); response.setHTTPStatusCode(SOUP_STATUS_FORBIDDEN); // ? client->didReceiveResponse(handle, response); client->didFinishLoading(handle); return; } response.setMimeType(g_file_info_get_content_type(info)); response.setExpectedContentLength(g_file_info_get_size(info)); response.setHTTPStatusCode(SOUP_STATUS_OK); 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, handle); }