MultipartHandle.cpp [plain text]
#include "config.h"
#include "MultipartHandle.h"
#if USE(CURL)
#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceResponse.h"
#include <wtf/StringExtras.h>
namespace WebCore {
bool MultipartHandle::extractBoundary(const String& contentType, String& boundary)
{
static const size_t length = strlen("boundary=");
size_t boundaryStart = contentType.findIgnoringCase("boundary=");
if (boundaryStart == notFound)
return false;
boundaryStart += length;
size_t boundaryEnd = 0;
if (contentType[boundaryStart] == '"') {
++boundaryStart;
boundaryEnd = contentType.find('"', boundaryStart);
if (boundaryEnd == notFound)
return false;
} else if (contentType[boundaryStart] == '\'') {
++boundaryStart;
boundaryEnd = contentType.find('\'', boundaryStart);
if (boundaryEnd == notFound)
return false;
} else {
boundaryEnd = contentType.find(';', boundaryStart);
if (boundaryEnd == notFound)
boundaryEnd = contentType.length();
}
if (boundaryEnd <= boundaryStart)
return false;
boundary = contentType.substring(boundaryStart, boundaryEnd - boundaryStart);
return true;
}
bool MultipartHandle::matchForBoundary(const char* data, size_t position, size_t& matchedLength)
{
matchedLength = 0;
for (size_t i = 0; i < m_boundaryLength; ++i) {
if (data[position + i] != m_boundary[i]) {
matchedLength = i;
return false;
}
}
matchedLength = m_boundaryLength;
return true;
}
bool MultipartHandle::checkForBoundary(size_t& boundaryStartPosition, size_t& lastPartialMatchPosition)
{
size_t contentLength = m_buffer.size();
boundaryStartPosition = notFound;
lastPartialMatchPosition = contentLength;
if (contentLength < m_boundaryLength)
return false;
const char* content = m_buffer.data();
size_t matched;
for (size_t i = 0; i < contentLength - m_boundaryLength; ++i) {
if (matchForBoundary(content, i, matched)) {
boundaryStartPosition = i;
return true;
}
if (matched)
lastPartialMatchPosition = i;
}
return false;
}
bool MultipartHandle::parseHeadersIfPossible()
{
size_t contentLength = m_buffer.size();
if (!contentLength)
return false;
const char* content = m_buffer.data();
if (!strnstr(content, "\r\n\r\n", contentLength)) {
if (!strnstr(content, "\n\n", contentLength)) {
return false;
}
}
String value;
StringView name;
char* p = const_cast<char*>(content);
const char* end = content + contentLength;
size_t totalConsumedLength = 0;
for (; p < end; ++p) {
String failureReason;
size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value, false);
if (!consumedLength)
break;
p += consumedLength;
totalConsumedLength += consumedLength;
if (name.isEmpty())
break;
m_headers.add(name.toString(), value);
}
m_buffer.remove(0, totalConsumedLength + 1);
return true;
}
void MultipartHandle::contentReceived(const char* data, size_t length)
{
if (m_state == End)
return;
m_buffer.append(data, length);
while (processContent()) { }
}
bool MultipartHandle::processContent()
{
switch (m_state) {
case CheckBoundary: {
if (m_buffer.size() < m_boundaryLength) {
return false;
}
size_t boundaryStart;
size_t lastPartialMatch;
if (!checkForBoundary(boundaryStart, lastPartialMatch) && boundaryStart == notFound) {
m_buffer.remove(0, lastPartialMatch);
return false;
}
m_buffer.remove(0, boundaryStart + m_boundaryLength);
m_state = InBoundary;
}
case InBoundary: {
if (m_buffer.size() < 2)
return false;
const char* content = m_buffer.data();
size_t removeCount = 2;
if (content[0] != '\r' || content[1] != '\n') {
if (content[0] != '\n') {
m_state = End;
return false;
}
removeCount = 1;
}
m_buffer.remove(0, removeCount);
m_headers.clear();
m_state = InHeader;
}
case InHeader: {
if (!parseHeadersIfPossible()) {
return false;
}
didReceiveResponse();
m_state = InContent;
}
case InContent: {
if (m_buffer.isEmpty())
return false;
size_t boundaryStart = notFound;
size_t lastPartialMatch;
if (!checkForBoundary(boundaryStart, lastPartialMatch) && boundaryStart == notFound) {
didReceiveData(lastPartialMatch);
m_buffer.remove(0, lastPartialMatch);
return false;
}
didReceiveData(boundaryStart);
m_buffer.remove(0, boundaryStart + m_boundaryLength);
m_state = EndBoundary;
}
case EndBoundary: {
if (m_buffer.size() < 2)
return false;
const char* content = m_buffer.data();
if (content[0] == '-' && content[1] == '-') {
m_state = End;
return false;
}
m_state = InBoundary;
break;
}
case End:
return false;
default:
ASSERT_NOT_REACHED();
return false;
}
return true; }
void MultipartHandle::contentEnded()
{
while (processContent()) { }
if (m_state != End) {
didReceiveData(m_buffer.size());
m_state = End;
}
m_buffer.clear();
}
void MultipartHandle::didReceiveData(size_t length)
{
ResourceHandleInternal* d = m_resourceHandle->getInternal();
if (d->m_cancelled) {
m_state = End;
return;
}
if (d->client()) {
const char* data = m_buffer.data();
d->client()->didReceiveData(m_resourceHandle, data, length, length);
}
}
void MultipartHandle::didReceiveResponse()
{
ResourceHandleInternal* d = m_resourceHandle->getInternal();
if (d->client()) {
auto response = d->m_response;
HTTPHeaderMap::const_iterator end = m_headers.end();
for (HTTPHeaderMap::const_iterator it = m_headers.begin(); it != end; ++it)
response.setHTTPHeaderField(it->key, it->value);
String contentType = m_headers.get(HTTPHeaderName::ContentType);
String mimeType = extractMIMETypeFromMediaType(contentType);
response.setMimeType(mimeType.convertToASCIILowercase());
response.setTextEncodingName(extractCharsetFromMediaType(contentType));
d->client()->didReceiveResponse(m_resourceHandle, WTFMove(response));
}
}
}
#endif