1/* 2 * Copyright (C) 2009 Gustavo Noronha Silva 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21 22#if USE(SOUP) 23 24#include "ResourceRequest.h" 25 26#include "GUniquePtrSoup.h" 27#include "HTTPParsers.h" 28#include "MIMETypeRegistry.h" 29#include <wtf/text/CString.h> 30#include <wtf/text/WTFString.h> 31 32namespace WebCore { 33 34void ResourceRequest::updateSoupMessageMembers(SoupMessage* soupMessage) const 35{ 36 updateSoupMessageHeaders(soupMessage->request_headers); 37 38 GUniquePtr<SoupURI> firstParty = firstPartyForCookies().createSoupURI(); 39 if (firstParty) 40 soup_message_set_first_party(soupMessage, firstParty.get()); 41 42 soup_message_set_flags(soupMessage, m_soupFlags); 43 44 if (!acceptEncoding()) 45 soup_message_disable_feature(soupMessage, SOUP_TYPE_CONTENT_DECODER); 46 if (!allowCookies()) 47 soup_message_disable_feature(soupMessage, SOUP_TYPE_COOKIE_JAR); 48} 49 50void ResourceRequest::updateSoupMessageHeaders(SoupMessageHeaders* soupHeaders) const 51{ 52 const HTTPHeaderMap& headers = httpHeaderFields(); 53 if (!headers.isEmpty()) { 54 HTTPHeaderMap::const_iterator end = headers.end(); 55 for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) 56 soup_message_headers_append(soupHeaders, it->key.utf8().data(), it->value.utf8().data()); 57 } 58} 59 60void ResourceRequest::updateFromSoupMessageHeaders(SoupMessageHeaders* soupHeaders) 61{ 62 m_httpHeaderFields.clear(); 63 SoupMessageHeadersIter headersIter; 64 soup_message_headers_iter_init(&headersIter, soupHeaders); 65 const char* headerName; 66 const char* headerValue; 67 while (soup_message_headers_iter_next(&headersIter, &headerName, &headerValue)) 68 m_httpHeaderFields.set(String::fromUTF8(headerName), String::fromUTF8(headerValue)); 69} 70 71void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const 72{ 73 g_object_set(soupMessage, SOUP_MESSAGE_METHOD, httpMethod().utf8().data(), NULL); 74 75 GUniquePtr<SoupURI> uri = createSoupURI(); 76 soup_message_set_uri(soupMessage, uri.get()); 77 78 updateSoupMessageMembers(soupMessage); 79} 80 81SoupMessage* ResourceRequest::toSoupMessage() const 82{ 83 SoupMessage* soupMessage = soup_message_new(httpMethod().utf8().data(), url().string().utf8().data()); 84 if (!soupMessage) 85 return 0; 86 87 updateSoupMessageMembers(soupMessage); 88 89 // Body data is only handled at ResourceHandleSoup::startHttp for 90 // now; this is because this may not be a good place to go 91 // openning and mmapping files. We should maybe revisit this. 92 return soupMessage; 93} 94 95void ResourceRequest::updateFromSoupMessage(SoupMessage* soupMessage) 96{ 97 bool shouldPortBeResetToZero = m_url.hasPort() && !m_url.port(); 98 m_url = URL(soup_message_get_uri(soupMessage)); 99 100 // SoupURI cannot differeniate between an explicitly specified port 0 and 101 // no port specified. 102 if (shouldPortBeResetToZero) 103 m_url.setPort(0); 104 105 m_httpMethod = String::fromUTF8(soupMessage->method); 106 107 updateFromSoupMessageHeaders(soupMessage->request_headers); 108 109 if (soupMessage->request_body->data) 110 m_httpBody = FormData::create(soupMessage->request_body->data, soupMessage->request_body->length); 111 112 if (SoupURI* firstParty = soup_message_get_first_party(soupMessage)) 113 m_firstPartyForCookies = URL(firstParty); 114 115 m_soupFlags = soup_message_get_flags(soupMessage); 116 117 // FIXME: m_allowCookies should probably be handled here and on 118 // doUpdatePlatformRequest somehow. 119} 120 121static const char* gSoupRequestInitiatingPageIDKey = "wk-soup-request-initiating-page-id"; 122 123void ResourceRequest::updateSoupRequest(SoupRequest* soupRequest) const 124{ 125 if (!m_initiatingPageID) 126 return; 127 128 uint64_t* initiatingPageIDPtr = static_cast<uint64_t*>(fastMalloc(sizeof(uint64_t))); 129 *initiatingPageIDPtr = m_initiatingPageID; 130 g_object_set_data_full(G_OBJECT(soupRequest), g_intern_static_string(gSoupRequestInitiatingPageIDKey), initiatingPageIDPtr, fastFree); 131} 132 133void ResourceRequest::updateFromSoupRequest(SoupRequest* soupRequest) 134{ 135 uint64_t* initiatingPageIDPtr = static_cast<uint64_t*>(g_object_get_data(G_OBJECT(soupRequest), gSoupRequestInitiatingPageIDKey)); 136 m_initiatingPageID = initiatingPageIDPtr ? *initiatingPageIDPtr : 0; 137} 138 139unsigned initializeMaximumHTTPConnectionCountPerHost() 140{ 141 // Soup has its own queue control; it wants to have all requests 142 // given to it, so that it is able to look ahead, and schedule 143 // them in a good way. 144 return 10000; 145} 146 147GUniquePtr<SoupURI> ResourceRequest::createSoupURI() const 148{ 149 // WebKit does not support fragment identifiers in data URLs, but soup does. 150 // Before passing the URL to soup, we should make sure to urlencode any '#' 151 // characters, so that soup does not interpret them as fragment identifiers. 152 // See http://wkbug.com/68089 153 if (m_url.protocolIsData()) { 154 String urlString = m_url.string(); 155 urlString.replace("#", "%23"); 156 return GUniquePtr<SoupURI>(soup_uri_new(urlString.utf8().data())); 157 } 158 159 GUniquePtr<SoupURI> soupURI; 160 if (m_url.hasFragmentIdentifier()) { 161 URL url = m_url; 162 url.removeFragmentIdentifier(); 163 soupURI.reset(soup_uri_new(url.string().utf8().data())); 164 } else 165 soupURI = m_url.createSoupURI(); 166 167 // Versions of libsoup prior to 2.42 have a soup_uri_new that will convert empty passwords that are not 168 // prefixed by a colon into null. Some parts of soup like the SoupAuthenticationManager will only be active 169 // when both the username and password are non-null. When we have credentials, empty usernames and passwords 170 // should be empty strings instead of null. 171 String urlUser = m_url.user(); 172 String urlPass = m_url.pass(); 173 if (!urlUser.isEmpty() || !urlPass.isEmpty()) { 174 soup_uri_set_user(soupURI.get(), urlUser.utf8().data()); 175 soup_uri_set_password(soupURI.get(), urlPass.utf8().data()); 176 } 177 178 return soupURI; 179} 180 181} 182 183#endif 184