Add Generic HTTP Request Class. Generalized HTTP Parsing.
authorjocelyn.turcotte@nokia.com <jocelyn.turcotte@nokia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Apr 2012 13:23:23 +0000 (13:23 +0000)
committerjocelyn.turcotte@nokia.com <jocelyn.turcotte@nokia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Apr 2012 13:23:23 +0000 (13:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=73092

Reviewed by Simon Hausmann.

A WebSocket server will need to read HTTP Requests before upgrading
to WebSocket framing. This patch creates a cross-platform HTTPRequest
class. Since some of this was already done in WebSocket code:

  - the header parsing code was extracted and put into HTTPParsers.cpp
  - WebSocketHandshakeRequest subclasses HTTPRequest
  - WebSocket code was refactored for these changes

Refactoring is covered by existing http/tests/websocket tests.

* CMakeLists.txt:
* GNUmakefile.list.am:

* Modules/websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::didOpenSocketStream):

  Extract the HTTP Header parsing out of WebSocketHandshake and
  put it into HTTP Parsers. Now make use of the generic parsing.

* Modules/websockets/WebSocketHandshake.cpp:
(WebCore):
(WebCore::trimInputSample):
(WebCore::WebSocketHandshake::clientHandshakeRequest): switch to RefCounted HandshakeRequest.
(WebCore::WebSocketHandshake::readStatusLine):
(WebCore::WebSocketHandshake::readHTTPHeaders): use generic HTTPParsers parsing.
* Modules/websockets/WebSocketHandshake.h: switch to RefCounted HandshakeRequest.

  WebSocketHandshakeRequest is a specialization of an HTTPRequest.
  Much of its original code was abstracted into HTTPRequest, and
  this is now a subclass with WebSocket key information.

* Modules/websockets/WebSocketHandshakeRequest.cpp:
(WebCore::WebSocketHandshakeRequest::WebSocketHandshakeRequest): initialize now calls super.
* Modules/websockets/WebSocketHandshakeRequest.h:
(WebCore::WebSocketHandshakeRequest::create): switch to being ref counted like HTTPRequest.
(WebSocketHandshakeRequest):

* Target.pri:
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:

  Add generic HTTP Request parsing functions to HTTPParsers.cpp.
  Use out parameters to get values after parsing.

* platform/network/HTTPParsers.cpp:
(WebCore):
(WebCore::trimInputSample): taken from WebSocket parsing.
(WebCore::parseHTTPRequestLine): new code, parse a request line.
(WebCore::parseHTTPHeaders): taken from previous WebSocket parsing. Algorithm unchanged.
(WebCore::parseHTTPRequestBody): new code, just copy the date into the provided buffer.
* platform/network/HTTPParsers.h:
(WebCore):

  This adds a general HTTPRequest class. Accessors for the
  request method, url, httpVersion, headers, and data. You can
  create an HTTPRequest using the constructor, or parse one
  from a data buffer, such as one read from a socket.

* platform/network/HTTPRequest.cpp: Added.
(WebCore):
(WebCore::HTTPRequest::parseHTTPRequestFromBuffer): create an HTTPRequest by parsing data in a buffer.
(WebCore::HTTPRequest::parseRequestLine): helper to initialize members while parsing.
(WebCore::HTTPRequest::parseHeaders): helper to initialize members while parsing.
(WebCore::HTTPRequest::parseRequestBody): helper to initialize members while parsing.
(WebCore::HTTPRequest::HTTPRequest):
(WebCore::HTTPRequest::~HTTPRequest):
* platform/network/HTTPRequest.h: Added.
(WebCore):
(HTTPRequest):
(WebCore::HTTPRequest::create): create an empty request, or specify details.
(WebCore::HTTPRequest::requestMethod):
(WebCore::HTTPRequest::setRequestMethod):
(WebCore::HTTPRequest::url):
(WebCore::HTTPRequest::setURL):
(WebCore::HTTPRequest::body):
(WebCore::HTTPRequest::headerFields):
(WebCore::HTTPRequest::addHeaderField):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@113024 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Modules/websockets/WebSocketChannel.cpp
Source/WebCore/Modules/websockets/WebSocketHandshake.cpp
Source/WebCore/Modules/websockets/WebSocketHandshake.h
Source/WebCore/Modules/websockets/WebSocketHandshakeRequest.cpp
Source/WebCore/Modules/websockets/WebSocketHandshakeRequest.h
Source/WebCore/Target.pri
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/network/HTTPParsers.cpp
Source/WebCore/platform/network/HTTPParsers.h
Source/WebCore/platform/network/HTTPRequest.cpp [new file with mode: 0644]
Source/WebCore/platform/network/HTTPRequest.h [new file with mode: 0644]

index 42a3282..05fa0af 100644 (file)
@@ -1227,6 +1227,7 @@ SET(WebCore_SOURCES
     platform/network/FormData.cpp
     platform/network/HTTPHeaderMap.cpp
     platform/network/HTTPParsers.cpp
+    platform/network/HTTPRequest.cpp
     platform/network/HTTPValidation.cpp
     platform/network/MIMEHeader.cpp
     platform/network/NetworkStateNotifier.cpp
index 15ca9b7..1a933ba 100644 (file)
@@ -1,3 +1,89 @@
+2012-03-29  Joseph Pecoraro  <joepeck@webkit.org> and Jocelyn Turcotte  <jocelyn.turcotte@nokia.com>
+
+        Add Generic HTTP Request Class. Generalized HTTP Parsing.
+        https://bugs.webkit.org/show_bug.cgi?id=73092
+
+        Reviewed by Simon Hausmann.
+
+        A WebSocket server will need to read HTTP Requests before upgrading
+        to WebSocket framing. This patch creates a cross-platform HTTPRequest
+        class. Since some of this was already done in WebSocket code:
+
+          - the header parsing code was extracted and put into HTTPParsers.cpp
+          - WebSocketHandshakeRequest subclasses HTTPRequest
+          - WebSocket code was refactored for these changes
+
+        Refactoring is covered by existing http/tests/websocket tests.
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+
+        * Modules/websockets/WebSocketChannel.cpp:
+        (WebCore::WebSocketChannel::didOpenSocketStream):
+
+          Extract the HTTP Header parsing out of WebSocketHandshake and
+          put it into HTTP Parsers. Now make use of the generic parsing.
+
+        * Modules/websockets/WebSocketHandshake.cpp:
+        (WebCore):
+        (WebCore::trimInputSample):
+        (WebCore::WebSocketHandshake::clientHandshakeRequest): switch to RefCounted HandshakeRequest.
+        (WebCore::WebSocketHandshake::readStatusLine):
+        (WebCore::WebSocketHandshake::readHTTPHeaders): use generic HTTPParsers parsing.
+        * Modules/websockets/WebSocketHandshake.h: switch to RefCounted HandshakeRequest.
+
+          WebSocketHandshakeRequest is a specialization of an HTTPRequest.
+          Much of its original code was abstracted into HTTPRequest, and
+          this is now a subclass with WebSocket key information.
+
+        * Modules/websockets/WebSocketHandshakeRequest.cpp:
+        (WebCore::WebSocketHandshakeRequest::WebSocketHandshakeRequest): initialize now calls super.
+        * Modules/websockets/WebSocketHandshakeRequest.h:
+        (WebCore::WebSocketHandshakeRequest::create): switch to being ref counted like HTTPRequest.
+        (WebSocketHandshakeRequest):
+
+        * Target.pri:
+        * WebCore.gypi:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+
+          Add generic HTTP Request parsing functions to HTTPParsers.cpp.
+          Use out parameters to get values after parsing.
+
+        * platform/network/HTTPParsers.cpp:
+        (WebCore):
+        (WebCore::trimInputSample): taken from WebSocket parsing.
+        (WebCore::parseHTTPRequestLine): new code, parse a request line.
+        (WebCore::parseHTTPHeaders): taken from previous WebSocket parsing. Algorithm unchanged.
+        (WebCore::parseHTTPRequestBody): new code, just copy the date into the provided buffer.
+        * platform/network/HTTPParsers.h:
+        (WebCore):
+
+          This adds a general HTTPRequest class. Accessors for the
+          request method, url, httpVersion, headers, and data. You can
+          create an HTTPRequest using the constructor, or parse one
+          from a data buffer, such as one read from a socket.
+
+        * platform/network/HTTPRequest.cpp: Added.
+        (WebCore):
+        (WebCore::HTTPRequest::parseHTTPRequestFromBuffer): create an HTTPRequest by parsing data in a buffer.
+        (WebCore::HTTPRequest::parseRequestLine): helper to initialize members while parsing.
+        (WebCore::HTTPRequest::parseHeaders): helper to initialize members while parsing.
+        (WebCore::HTTPRequest::parseRequestBody): helper to initialize members while parsing.
+        (WebCore::HTTPRequest::HTTPRequest):
+        (WebCore::HTTPRequest::~HTTPRequest):
+        * platform/network/HTTPRequest.h: Added.
+        (WebCore):
+        (HTTPRequest):
+        (WebCore::HTTPRequest::create): create an empty request, or specify details.
+        (WebCore::HTTPRequest::requestMethod):
+        (WebCore::HTTPRequest::setRequestMethod):
+        (WebCore::HTTPRequest::url):
+        (WebCore::HTTPRequest::setURL):
+        (WebCore::HTTPRequest::body):
+        (WebCore::HTTPRequest::headerFields):
+        (WebCore::HTTPRequest::addHeaderField):
+
 2012-04-03  Jocelyn Turcotte  <jocelyn.turcotte@nokia.com>
 
         [Qt] Add a missing inspector front-end image to resources.
index 5a1e67d..672f5b0 100644 (file)
@@ -3417,6 +3417,8 @@ webcore_sources += \
        Source/WebCore/platform/network/HTTPHeaderMap.h \
        Source/WebCore/platform/network/HTTPParsers.cpp \
        Source/WebCore/platform/network/HTTPParsers.h \
+       Source/WebCore/platform/network/HTTPRequest.cpp \
+       Source/WebCore/platform/network/HTTPRequest.h \
        Source/WebCore/platform/network/HTTPValidation.cpp \
        Source/WebCore/platform/network/HTTPValidation.h \
        Source/WebCore/platform/network/MIMEHeader.cpp \
index 104813b..7156953 100644 (file)
@@ -283,7 +283,7 @@ void WebSocketChannel::didOpenSocketStream(SocketStreamHandle* handle)
     if (!m_document)
         return;
     if (m_identifier)
-        InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_document, m_identifier, m_handshake->clientHandshakeRequest());
+        InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_document, m_identifier, *m_handshake->clientHandshakeRequest());
     CString handshakeMessage = m_handshake->clientHandshakeMessage();
     if (!handle->send(handshakeMessage.data(), handshakeMessage.length()))
         fail("Failed to send WebSocket handshake.");
index 4f32d6c..823a09e 100644 (file)
@@ -41,6 +41,7 @@
 #include "CookieJar.h"
 #include "Document.h"
 #include "HTTPHeaderMap.h"
+#include "HTTPParsers.h"
 #include "KURL.h"
 #include "Logging.h"
 #include "ScriptCallStack.h"
@@ -85,11 +86,11 @@ static String hostName(const KURL& url, bool secure)
     return builder.toString();
 }
 
-static const size_t maxConsoleMessageSize = 128;
-static String trimConsoleMessage(const char* p, size_t len)
+static const size_t maxInputSampleSize = 128;
+static String trimInputSample(const char* p, size_t len)
 {
-    String s = String(p, std::min<size_t>(len, maxConsoleMessageSize));
-    if (len > maxConsoleMessageSize)
+    String s = String(p, std::min<size_t>(len, maxInputSampleSize));
+    if (len > maxInputSampleSize)
         s.append(horizontalEllipsis);
     return s;
 }
@@ -316,44 +317,44 @@ CString WebSocketHandshake::clientHandshakeMessage() const
     return msg;
 }
 
-WebSocketHandshakeRequest WebSocketHandshake::clientHandshakeRequest() const
+PassRefPtr<WebSocketHandshakeRequest> WebSocketHandshake::clientHandshakeRequest() const
 {
     // Keep the following consistent with clientHandshakeMessage().
     // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
     // m_key3 in WebSocketHandshakeRequest?
-    WebSocketHandshakeRequest request("GET", m_url);
+    RefPtr<WebSocketHandshakeRequest> request = WebSocketHandshakeRequest::create("GET", m_url);
     if (m_useHixie76Protocol)
-        request.addHeaderField("Upgrade", "WebSocket");
+        request->addHeaderField("Upgrade", "WebSocket");
     else
-        request.addHeaderField("Upgrade", "websocket");
-    request.addHeaderField("Connection", "Upgrade");
-    request.addHeaderField("Host", hostName(m_url, m_secure));
-    request.addHeaderField("Origin", clientOrigin());
+        request->addHeaderField("Upgrade", "websocket");
+    request->addHeaderField("Connection", "Upgrade");
+    request->addHeaderField("Host", hostName(m_url, m_secure));
+    request->addHeaderField("Origin", clientOrigin());
     if (!m_clientProtocol.isEmpty())
-        request.addHeaderField("Sec-WebSocket-Protocol:", m_clientProtocol);
+        request->addHeaderField("Sec-WebSocket-Protocol", m_clientProtocol);
 
     KURL url = httpURLForAuthenticationAndCookies();
     if (m_context->isDocument()) {
         Document* document = static_cast<Document*>(m_context);
         String cookie = cookieRequestHeaderFieldValue(document, url);
         if (!cookie.isEmpty())
-            request.addHeaderField("Cookie", cookie);
+            request->addHeaderField("Cookie", cookie);
         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
     }
 
     if (m_useHixie76Protocol) {
-        request.addHeaderField("Sec-WebSocket-Key1", m_hixie76SecWebSocketKey1);
-        request.addHeaderField("Sec-WebSocket-Key2", m_hixie76SecWebSocketKey2);
-        request.setKey3(m_hixie76Key3);
+        request->addHeaderField("Sec-WebSocket-Key1", m_hixie76SecWebSocketKey1);
+        request->addHeaderField("Sec-WebSocket-Key2", m_hixie76SecWebSocketKey2);
+        request->setKey3(m_hixie76Key3);
     } else {
-        request.addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
-        request.addHeaderField("Sec-WebSocket-Version", "13");
+        request->addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
+        request->addHeaderField("Sec-WebSocket-Version", "13");
         const String extensionValue = m_extensionDispatcher.createHeaderValue();
         if (extensionValue.length())
-            request.addHeaderField("Sec-WebSocket-Extensions", extensionValue);
+            request->addHeaderField("Sec-WebSocket-Extensions", extensionValue);
     }
 
-    return request;
+    return request.release();
 }
 
 void WebSocketHandshake::reset()
@@ -549,7 +550,7 @@ int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength,
     }
 
     if (!space1 || !space2) {
-        m_failureReason = "No response code found: " + trimConsoleMessage(header, lineLength - 2);
+        m_failureReason = "No response code found: " + trimInputSample(header, lineLength - 2);
         return lineLength;
     }
 
@@ -574,98 +575,46 @@ const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* e
 {
     m_response.clearHeaderFields();
 
-    Vector<char> name;
-    Vector<char> value;
+    AtomicString name;
+    String value;
     bool sawSecWebSocketAcceptHeaderField = false;
     bool sawSecWebSocketProtocolHeaderField = false;
-    for (const char* p = start; p < end; p++) {
-        name.clear();
-        value.clear();
-
-        for (; p < end; p++) {
-            switch (*p) {
-            case '\r':
-                if (name.isEmpty()) {
-                    if (p + 1 < end && *(p + 1) == '\n')
-                        return p + 2;
-                    m_failureReason = "CR doesn't follow LF at " + trimConsoleMessage(p, end - p);
-                    return 0;
-                }
-                m_failureReason = "Unexpected CR in name at " + trimConsoleMessage(name.data(), name.size());
-                return 0;
-            case '\n':
-                m_failureReason = "Unexpected LF in name at " + trimConsoleMessage(name.data(), name.size());
-                return 0;
-            case ':':
-                break;
-            default:
-                name.append(*p);
-                continue;
-            }
-            if (*p == ':') {
-                ++p;
-                break;
-            }
-        }
+    const char* p = start;
+    for (; p < end; p++) {
+        size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value);
+        if (!consumedLength)
+            return 0;
+        p += consumedLength;
 
-        for (; p < end && *p == 0x20; p++) { }
+        // Stop once we consumed an empty line.
+        if (name.isEmpty())
+            break;
 
-        for (; p < end; p++) {
-            switch (*p) {
-            case '\r':
-                break;
-            case '\n':
-                m_failureReason = "Unexpected LF in value at " + trimConsoleMessage(value.data(), value.size());
-                return 0;
-            default:
-                value.append(*p);
-            }
-            if (*p == '\r') {
-                ++p;
-                break;
-            }
-        }
-        if (p >= end || *p != '\n') {
-            m_failureReason = "CR doesn't follow LF after value at " + trimConsoleMessage(p, end - p);
-            return 0;
-        }
-        AtomicString nameStr = AtomicString::fromUTF8(name.data(), name.size());
-        String valueStr = String::fromUTF8(value.data(), value.size());
-        if (nameStr.isNull()) {
-            m_failureReason = "Invalid UTF-8 sequence in header name";
-            return 0;
-        }
-        if (valueStr.isNull()) {
-            m_failureReason = "Invalid UTF-8 sequence in header value";
-            return 0;
-        }
-        LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data());
         // Sec-WebSocket-Extensions may be split. We parse and check the
         // header value every time the header appears.
-        if (equalIgnoringCase("sec-websocket-extensions", nameStr)) {
-            if (!m_extensionDispatcher.processHeaderValue(valueStr)) {
+        if (equalIgnoringCase("sec-websocket-extensions", name)) {
+            if (!m_extensionDispatcher.processHeaderValue(value)) {
                 m_failureReason = m_extensionDispatcher.failureReason();
                 return 0;
             }
-        } else if (equalIgnoringCase("Sec-WebSocket-Accept", nameStr)) {
+        } else if (equalIgnoringCase("Sec-WebSocket-Accept", name)) {
             if (sawSecWebSocketAcceptHeaderField) {
                 m_failureReason = "The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response";
                 return 0;
             }
-            m_response.addHeaderField(nameStr, valueStr);
+            m_response.addHeaderField(name, value);
             sawSecWebSocketAcceptHeaderField = true;
-        } else if (equalIgnoringCase("Sec-WebSocket-Protocol", nameStr)) {
+        } else if (equalIgnoringCase("Sec-WebSocket-Protocol", name)) {
             if (sawSecWebSocketProtocolHeaderField) {
                 m_failureReason = "The Sec-WebSocket-Protocol header MUST NOT appear more than once in an HTTP response";
                 return 0;
             }
-            m_response.addHeaderField(nameStr, valueStr);
+            m_response.addHeaderField(name, value);
             sawSecWebSocketProtocolHeaderField = true;
         } else
-            m_response.addHeaderField(nameStr, valueStr);
+            m_response.addHeaderField(name, value);
     }
-    ASSERT_NOT_REACHED();
-    return 0;
+    return p;
 }
 
 bool WebSocketHandshake::checkResponseHeaders()
index b025f0c..b92cea0 100644 (file)
@@ -67,7 +67,7 @@ public:
     String clientLocation() const;
 
     CString clientHandshakeMessage() const;
-    WebSocketHandshakeRequest clientHandshakeRequest() const;
+    PassRefPtr<WebSocketHandshakeRequest> clientHandshakeRequest() const;
 
     void reset();
     void clearScriptExecutionContext();
index 1132a44..fdf7443 100644 (file)
@@ -51,8 +51,7 @@ void WebSocketHandshakeRequest::Key3::set(const unsigned char key3[8])
 }
 
 WebSocketHandshakeRequest::WebSocketHandshakeRequest(const String& requestMethod, const KURL& url)
-    : m_url(url)
-    , m_requestMethod(requestMethod)
+    : HTTPRequest(requestMethod, url, HTTP_1_1)
 {
 }
 
@@ -60,26 +59,6 @@ WebSocketHandshakeRequest::~WebSocketHandshakeRequest()
 {
 }
 
-String WebSocketHandshakeRequest::requestMethod() const
-{
-    return m_requestMethod;
-}
-
-KURL WebSocketHandshakeRequest::url() const
-{
-    return m_url;
-}
-
-void WebSocketHandshakeRequest::addHeaderField(const char* name, const String& value)
-{
-    m_headerFields.add(name, value);
-}
-
-const HTTPHeaderMap& WebSocketHandshakeRequest::headerFields() const
-{
-    return m_headerFields;
-}
-
 WebSocketHandshakeRequest::Key3 WebSocketHandshakeRequest::key3() const
 {
     return m_key3;
index 792f67e..0b5ef2c 100644 (file)
 
 #if ENABLE(WEB_SOCKETS)
 
-#include "HTTPHeaderMap.h"
-#include "KURL.h"
-#include "PlatformString.h"
+#include "HTTPRequest.h"
 
 namespace WebCore {
 
-class WebSocketHandshakeRequest {
+class WebSocketHandshakeRequest : public HTTPRequest {
 public:
-    WebSocketHandshakeRequest(const String& requestMethod, const KURL&);
+    static PassRefPtr<WebSocketHandshakeRequest> create(const String& requestMethod, const KURL& url) { return adoptRef(new WebSocketHandshakeRequest(requestMethod, url)); }
     ~WebSocketHandshakeRequest();
 
-    String requestMethod() const;
-    KURL url() const;
-
-    const HTTPHeaderMap& headerFields() const;
-    void addHeaderField(const char* name, const String& value);
-
     struct Key3 {
         unsigned char value[8];
 
@@ -60,9 +52,7 @@ public:
     void setKey3(const unsigned char key3[8]);
 
 private:
-    KURL m_url;
-    String m_requestMethod;
-    HTTPHeaderMap m_headerFields;
+    WebSocketHandshakeRequest(const String& requestMethod, const KURL&);
     Key3 m_key3;
 };
 
index 825eb25..af947f8 100644 (file)
@@ -1145,6 +1145,7 @@ SOURCES += \
     platform/network/FormDataBuilder.cpp \
     platform/network/HTTPHeaderMap.cpp \
     platform/network/HTTPParsers.cpp \
+    platform/network/HTTPRequest.cpp \
     platform/network/HTTPValidation.cpp \
     platform/network/MIMEHeader.cpp \
     platform/network/NetworkStateNotifier.cpp \
@@ -2288,6 +2289,7 @@ HEADERS += \
     platform/network/FormData.h \
     platform/network/HTTPHeaderMap.h \
     platform/network/HTTPParsers.h \
+    platform/network/HTTPRequest.h \
     platform/network/HTTPValidation.h \
     platform/network/HTTPStatusCodes.h \
     platform/network/MIMESniffing.h \
index 3d65af2..1004a5e 100644 (file)
             'platform/network/HTTPHeaderMap.cpp',
             'platform/network/HTTPParsers.cpp',
             'platform/network/HTTPParsers.h',
+            'platform/network/HTTPRequest.cpp',
+            'platform/network/HTTPRequest.h',
             'platform/network/HTTPValidation.cpp',
             'platform/network/HTTPValidation.h',
             'platform/network/MIMEHeader.cpp',
index f172d68..2117902 100755 (executable)
                                        >
                                </File>
                                <File
+                                       RelativePath="..\platform\network\HTTPRequest.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\platform\network\HTTPRequest.h"
+                                       >
+                               </File>
+                               <File
                                        RelativePath="..\platform\network\HTTPValidation.cpp"
                                        >
                                </File>
index ff1200e..0db41b4 100644 (file)
                A5ABB78713B904BC00F197E3 /* LineBreakIteratorPoolICU.h in Headers */ = {isa = PBXBuildFile; fileRef = A5ABB78613B904BC00F197E3 /* LineBreakIteratorPoolICU.h */; };
                A5AFB34F115151A700B045CB /* StepRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5AFB34D115151A700B045CB /* StepRange.cpp */; };
                A5AFB350115151A700B045CB /* StepRange.h in Headers */ = {isa = PBXBuildFile; fileRef = A5AFB34E115151A700B045CB /* StepRange.h */; };
+               A5D214B112E905510090F370 /* HTTPRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5D214AE12E905510090F370 /* HTTPRequest.cpp */; };
+               A5D214B212E905510090F370 /* HTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A5D214AF12E905510090F370 /* HTTPRequest.h */; };
                A6148A6212E41D3A0044A784 /* DOMHTMLKeygenElementInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = A6148A6112E41D3A0044A784 /* DOMHTMLKeygenElementInternal.h */; };
                A6148A6712E41D940044A784 /* DOMHTMLKeygenElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A6148A6512E41D940044A784 /* DOMHTMLKeygenElement.h */; };
                A6148A6812E41D940044A784 /* DOMHTMLKeygenElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = A6148A6612E41D940044A784 /* DOMHTMLKeygenElement.mm */; };
                A5AFB34E115151A700B045CB /* StepRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StepRange.h; sourceTree = "<group>"; };
                A5C974CF11485FF10066F2AB /* KeyEventCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeyEventCocoa.h; path = cocoa/KeyEventCocoa.h; sourceTree = "<group>"; };
                A5C974D011485FF10066F2AB /* KeyEventCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = KeyEventCocoa.mm; path = cocoa/KeyEventCocoa.mm; sourceTree = "<group>"; };
+               A5D214AE12E905510090F370 /* HTTPRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPRequest.cpp; sourceTree = "<group>"; };
+               A5D214AF12E905510090F370 /* HTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPRequest.h; sourceTree = "<group>"; };
                A6148A6112E41D3A0044A784 /* DOMHTMLKeygenElementInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMHTMLKeygenElementInternal.h; sourceTree = "<group>"; };
                A6148A6512E41D940044A784 /* DOMHTMLKeygenElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMHTMLKeygenElement.h; sourceTree = "<group>"; };
                A6148A6612E41D940044A784 /* DOMHTMLKeygenElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DOMHTMLKeygenElement.mm; sourceTree = "<group>"; };
                                514C765C0CE923A1007EF3CD /* HTTPHeaderMap.h */,
                                514C765D0CE923A1007EF3CD /* HTTPParsers.cpp */,
                                514C765E0CE923A1007EF3CD /* HTTPParsers.h */,
+                               A5D214AE12E905510090F370 /* HTTPRequest.cpp */,
+                               A5D214AF12E905510090F370 /* HTTPRequest.h */,
                                9B0FB18F140DB5790022588F /* HTTPValidation.cpp */,
                                9B0FB190140DB5790022588F /* HTTPValidation.h */,
                                37DDCD9213844FD50008B793 /* MIMEHeader.cpp */,
                                977B387A122883E900B81FF8 /* HTMLViewSourceParser.h in Headers */,
                                514C76710CE923A1007EF3CD /* HTTPHeaderMap.h in Headers */,
                                514C76730CE923A1007EF3CD /* HTTPParsers.h in Headers */,
+                               A5D214B212E905510090F370 /* HTTPRequest.h in Headers */,
                                9B0FB192140DB5790022588F /* HTTPValidation.h in Headers */,
                                375CD232119D43C800A2A859 /* Hyphenation.h in Headers */,
                                B275356E0B053814002CE64F /* Icon.h in Headers */,
                                977B3879122883E900B81FF8 /* HTMLViewSourceParser.cpp in Sources */,
                                0B8C56D40F28627F000502E1 /* HTTPHeaderMap.cpp in Sources */,
                                514C76720CE923A1007EF3CD /* HTTPParsers.cpp in Sources */,
+                               A5D214B112E905510090F370 /* HTTPRequest.cpp in Sources */,
                                9B0FB191140DB5790022588F /* HTTPValidation.cpp in Sources */,
                                371A67CB11C6C7DB00047B8B /* HyphenationCF.cpp in Sources */,
                                375CD23B119D44EA00A2A859 /* HyphenationMac.mm in Sources */,
index 8cbd4ff..9702132 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
  * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
  * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #include "config.h"
 #include "HTTPParsers.h"
-#include "ResourceResponseBase.h"
 
 #include "PlatformString.h"
+#include <wtf/DateMath.h>
 #include <wtf/text/CString.h>
 #include <wtf/text/StringBuilder.h>
-#include <wtf/DateMath.h>
+#include <wtf/unicode/CharacterNames.h>
 
 using namespace WTF;
 
@@ -90,6 +91,15 @@ bool isRFC2616Token(const String& characters)
     return true;
 }
 
+static const size_t maxInputSampleSize = 128;
+static String trimInputSample(const char* p, size_t length)
+{
+    String s = String(p, std::min<size_t>(length, maxInputSampleSize));
+    if (length > maxInputSampleSize)
+        s.append(horizontalEllipsis);
+    return s;
+}
+
 ContentDispositionType contentDispositionType(const String& contentDisposition)
 {
     if (contentDisposition.isEmpty())
@@ -395,4 +405,145 @@ bool parseRange(const String& range, long long& rangeOffset, long long& rangeEnd
     return true;
 }
 
+// HTTP/1.1 - RFC 2616
+// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1
+// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReason, String& method, String& url, HTTPVersion& httpVersion)
+{
+    method = String();
+    url = String();
+    httpVersion = Unknown;
+
+    const char* space1 = 0;
+    const char* space2 = 0;
+    const char* p;
+    size_t consumedLength;
+
+    for (p = data, consumedLength = 0; consumedLength < length; p++, consumedLength++) {
+        if (*p == ' ') {
+            if (!space1)
+                space1 = p;
+            else if (!space2)
+                space2 = p;
+        } else if (*p == '\n')
+            break;
+    }
+
+    // Haven't finished header line.
+    if (consumedLength == length) {
+        failureReason = "Incomplete Request Line";
+        return 0;
+    }
+
+    // RequestLine does not contain 3 parts.
+    if (!space1 || !space2) {
+        failureReason = "Request Line does not appear to contain: <Method> <Url> <HTTPVersion>.";
+        return 0;
+    }
+
+    // The line must end with "\r\n".
+    const char* end = p + 1;
+    if (*(end - 2) != '\r') {
+        failureReason = "Request line does not end with CRLF";
+        return 0;
+    }
+
+    // Request Method.
+    method = String(data, space1 - data); // For length subtract 1 for space, but add 1 for data being the first character.
+
+    // Request URI.
+    url = String(space1 + 1, space2 - space1 - 1); // For length subtract 1 for space.
+
+    // HTTP Version.
+    String httpVersionString(space2 + 1, end - space2 - 3); // For length subtract 1 for space, and 2 for "\r\n".
+    if (httpVersionString.length() != 8 || !httpVersionString.startsWith("HTTP/1."))
+        httpVersion = Unknown;
+    else if (httpVersionString[7] == '0')
+        httpVersion = HTTP_1_0;
+    else if (httpVersionString[7] == '1')
+        httpVersion = HTTP_1_1;
+    else
+        httpVersion = Unknown;
+
+    return end - data;
+}
+
+size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, AtomicString& nameStr, String& valueStr)
+{
+    const char* p = start;
+    const char* end = start + length;
+
+    Vector<char> name;
+    Vector<char> value;
+    nameStr = AtomicString();
+    valueStr = String();
+
+    for (; p < end; p++) {
+        switch (*p) {
+        case '\r':
+            if (name.isEmpty()) {
+                if (p + 1 < end && *(p + 1) == '\n')
+                    return (p + 2) - start;
+                failureReason = "CR doesn't follow LF at " + trimInputSample(p, end - p);
+                return 0;
+            }
+            failureReason = "Unexpected CR in name at " + trimInputSample(name.data(), name.size());
+            return 0;
+        case '\n':
+            failureReason = "Unexpected LF in name at " + trimInputSample(name.data(), name.size());
+            return 0;
+        case ':':
+            break;
+        default:
+            name.append(*p);
+            continue;
+        }
+        if (*p == ':') {
+            ++p;
+            break;
+        }
+    }
+
+    for (; p < end && *p == 0x20; p++) { }
+
+    for (; p < end; p++) {
+        switch (*p) {
+        case '\r':
+            break;
+        case '\n':
+            failureReason = "Unexpected LF in value at " + trimInputSample(value.data(), value.size());
+            return 0;
+        default:
+            value.append(*p);
+        }
+        if (*p == '\r') {
+            ++p;
+            break;
+        }
+    }
+    if (p >= end || *p != '\n') {
+        failureReason = "CR doesn't follow LF after value at " + trimInputSample(p, end - p);
+        return 0;
+    }
+    nameStr = AtomicString::fromUTF8(name.data(), name.size());
+    valueStr = String::fromUTF8(value.data(), value.size());
+    if (nameStr.isNull()) {
+        failureReason = "Invalid UTF-8 sequence in header name";
+        return 0;
+    }
+    if (valueStr.isNull()) {
+        failureReason = "Invalid UTF-8 sequence in header value";
+        return 0;
+    }
+    return p - start;
+}
+
+size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned char>& body)
+{
+    body.clear();
+    body.append(data, length);
+
+    return length;
+}
+
 }
index 3c808af..02ec13e 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
  * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #define HTTPParsers_h
 
 #include <wtf/Forward.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
+class HTTPHeaderMap;
 class ResourceResponseBase;
 
 enum XSSProtectionDisposition {
@@ -63,6 +66,12 @@ String extractReasonPhraseFromHTTPStatusLine(const String&);
 // -1 could be set to one of the return parameters to indicate the value is not specified.
 bool parseRange(const String&, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength);
 
+// Parsing Complete HTTP Messages.
+enum HTTPVersion { Unknown, HTTP_1_0, HTTP_1_1 };
+size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReason, String& method, String& url, HTTPVersion&);
+size_t parseHTTPHeader(const char* data, size_t length, String& failureReason, AtomicString& nameStr, String& valueStr);
+size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned char>& body);
+
 }
 
 #endif
diff --git a/Source/WebCore/platform/network/HTTPRequest.cpp b/Source/WebCore/platform/network/HTTPRequest.cpp
new file mode 100644 (file)
index 0000000..f4f9d4f
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HTTPRequest.h"
+
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+PassRefPtr<HTTPRequest> HTTPRequest::parseHTTPRequestFromBuffer(const char* data, size_t length, String& failureReason)
+{
+    if (!length) {
+        failureReason = "No data to parse.";
+        return 0;
+    }
+
+    // Request we will be building.
+    RefPtr<HTTPRequest> request = HTTPRequest::create();
+
+    // Advance a pointer through the data as needed.
+    const char* pos = data;
+    size_t remainingLength = length;
+
+    // 1. Parse Method + URL.
+    size_t requestLineLength = request->parseRequestLine(pos, remainingLength, failureReason);
+    if (!requestLineLength)
+        return 0;
+    pos += requestLineLength;
+    remainingLength -= requestLineLength;
+
+    // 2. Parse HTTP Headers.
+    size_t headersLength = request->parseHeaders(pos, remainingLength, failureReason);
+    if (!headersLength)
+        return 0;
+    pos += headersLength;
+    remainingLength -= headersLength;
+
+    // 3. Parse HTTP Data.
+    size_t dataLength = request->parseRequestBody(pos, remainingLength);
+    pos += dataLength;
+    remainingLength -= dataLength;
+
+    // We should have processed the entire input.
+    ASSERT(!remainingLength);
+    return request.release();
+}
+
+size_t HTTPRequest::parseRequestLine(const char* data, size_t length, String& failureReason)
+{
+    String url;
+    size_t result = parseHTTPRequestLine(data, length, failureReason, m_requestMethod, url, m_httpVersion);
+    m_url = KURL(KURL(), url);
+    return result;
+}
+
+size_t HTTPRequest::parseHeaders(const char* data, size_t length, String& failureReason)
+{
+    const char* p = data;
+    const char* end = data + length;
+    AtomicString name;
+    String value;
+    for (; p < data + length; p++) {
+        size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value);
+        if (!consumedLength)
+            return 0;
+        p += consumedLength;
+        if (name.isEmpty())
+            break;
+        m_headerFields.add(name, value);
+    }
+    return p - data;
+}
+
+size_t HTTPRequest::parseRequestBody(const char* data, size_t length)
+{
+    return parseHTTPRequestBody(data, length, m_body);
+}
+
+HTTPRequest::HTTPRequest()
+    : m_httpVersion(Unknown)
+{
+}
+
+HTTPRequest::HTTPRequest(const String& requestMethod, const KURL& url, HTTPVersion version)
+    : m_url(url)
+    , m_httpVersion(version)
+    , m_requestMethod(requestMethod)
+{
+}
+
+HTTPRequest::~HTTPRequest()
+{
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/HTTPRequest.h b/Source/WebCore/platform/network/HTTPRequest.h
new file mode 100644 (file)
index 0000000..33522e3
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 Google Inc.  All rights reserved.
+ * Copyright (C) 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTTPRequest_h
+#define HTTPRequest_h
+
+#include "HTTPHeaderMap.h"
+#include "HTTPParsers.h"
+#include "KURL.h"
+#include "PlatformString.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class HTTPRequest : public RefCounted<HTTPRequest> {
+public:
+    static PassRefPtr<HTTPRequest> create() { return adoptRef(new HTTPRequest()); }
+    static PassRefPtr<HTTPRequest> create(const String& requestMethod, const KURL& url, HTTPVersion version) { return adoptRef(new HTTPRequest(requestMethod, url, version)); }
+    static PassRefPtr<HTTPRequest> parseHTTPRequestFromBuffer(const char* data, size_t length, String& failureReason);
+    virtual ~HTTPRequest();
+
+    String requestMethod() const { return m_requestMethod; }
+    void setRequestMethod(const String& method) { m_requestMethod = method; }
+
+    KURL url() const { return m_url; }
+    void setURL(const KURL& url) { m_url = url; }
+
+    const Vector<unsigned char>& body() const { return m_body; }
+
+    const HTTPHeaderMap& headerFields() const { return m_headerFields; }
+    void addHeaderField(const AtomicString& name, const String& value) { m_headerFields.add(name, value); }
+    void addHeaderField(const char* name, const String& value) { m_headerFields.add(name, value); }
+
+protected:
+    HTTPRequest();
+    HTTPRequest(const String& requestMethod, const KURL&, HTTPVersion);
+
+    // Parsing helpers.
+    size_t parseRequestLine(const char* data, size_t length, String& failureReason);
+    size_t parseHeaders(const char* data, size_t length, String& failureReason);
+    size_t parseRequestBody(const char* data, size_t length);
+
+    KURL m_url;
+    HTTPVersion m_httpVersion;
+    String m_requestMethod;
+    HTTPHeaderMap m_headerFields;
+    Vector<unsigned char> m_body;
+};
+
+} // namespace WebCore
+
+#endif // HTTPRequest_h