Commit d60141d7 authored by ukai@chromium.org's avatar ukai@chromium.org

2010-03-03 Yuta Kitamura <yutak@chromium.org>

        Reviewed by Alexey Proskuryakov.

        Add a new class that stores information about Web Socket handshake request.

        Instances of this class contain the necessary information to send a Web Socket
        handshake request. In the future, this class will provide request information
        to the Web Inspector.

        WebSocketHandshake needs to provide request information
        https://bugs.webkit.org/show_bug.cgi?id=34784

        No new tests, since the current tests will suffice (LayoutTests/websocket/*).

        * GNUmakefile.am:
        * WebCore.gypi:
        * WebCore.pro:
        * WebCore.vcproj/WebCore.vcproj:
        * WebCore.xcodeproj/project.pbxproj:
        * websockets/WebSocketChannel.cpp:
        * websockets/WebSocketHandshake.cpp:
        (WebCore::WebSocketHandshake::clientHandshakeMessage):
        (WebCore::WebSocketHandshake::clientHandshakeRequest):
        * websockets/WebSocketHandshake.h:
        * websockets/WebSocketHandshakeRequest.cpp: Added.
        (WebCore::WebSocketHandshakeRequest::WebSocketHandshakeRequest):
        (WebCore::WebSocketHandshakeRequest::~WebSocketHandshakeRequest):
        (WebCore::WebSocketHandshakeRequest::addExtraHeaderField):
        (WebCore::WebSocketHandshakeRequest::headerFields):
        (WebCore::WebSocketHandshakeRequest::host):
        * websockets/WebSocketHandshakeRequest.h: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@55505 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent f0abe7b4
2010-03-03 Yuta Kitamura <yutak@chromium.org>
Reviewed by Alexey Proskuryakov.
Add a new class that stores information about Web Socket handshake request.
Instances of this class contain the necessary information to send a Web Socket
handshake request. In the future, this class will provide request information
to the Web Inspector.
WebSocketHandshake needs to provide request information
https://bugs.webkit.org/show_bug.cgi?id=34784
No new tests, since the current tests will suffice (LayoutTests/websocket/*).
* GNUmakefile.am:
* WebCore.gypi:
* WebCore.pro:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* websockets/WebSocketChannel.cpp:
* websockets/WebSocketHandshake.cpp:
(WebCore::WebSocketHandshake::clientHandshakeMessage):
(WebCore::WebSocketHandshake::clientHandshakeRequest):
* websockets/WebSocketHandshake.h:
* websockets/WebSocketHandshakeRequest.cpp: Added.
(WebCore::WebSocketHandshakeRequest::WebSocketHandshakeRequest):
(WebCore::WebSocketHandshakeRequest::~WebSocketHandshakeRequest):
(WebCore::WebSocketHandshakeRequest::addExtraHeaderField):
(WebCore::WebSocketHandshakeRequest::headerFields):
(WebCore::WebSocketHandshakeRequest::host):
* websockets/WebSocketHandshakeRequest.h: Added.
2010-03-03 Laszlo Gombos <laszlo.1.gombos@nokia.com>
Reviewed by Kenneth Rohde Christiansen.
......
......@@ -3472,7 +3472,9 @@ webcore_sources += \
WebCore/websockets/WebSocketChannel.h \
WebCore/websockets/WebSocketChannelClient.h \
WebCore/websockets/WebSocketHandshake.cpp \
WebCore/websockets/WebSocketHandshake.h
WebCore/websockets/WebSocketHandshake.h \
WebCore/websockets/WebSocketHandshakeRequest.cpp \
WebCore/websockets/WebSocketHandshakeRequest.h
if ENABLE_WORKERS
webcore_sources += \
......
......@@ -3624,6 +3624,8 @@
'websockets/WebSocketChannelClient.h',
'websockets/WebSocketHandshake.cpp',
'websockets/WebSocketHandshake.h',
'websockets/WebSocketHandshakeRequest.cpp',
'websockets/WebSocketHandshakeRequest.h',
'websockets/WorkerThreadableWebSocketChannel.cpp',
'websockets/WorkerThreadableWebSocketChannel.h',
'workers/AbstractWorker.cpp',
......
......@@ -2673,24 +2673,35 @@ SOURCES += \
}
contains(DEFINES, ENABLE_WEB_SOCKETS=1) {
HEADERS += \
platform/network/qt/SocketStreamHandlePrivate.h \
HEADERS += \
websockets/ThreadableWebSocketChannel.h \
websockets/ThreadableWebSocketChannelClientWrapper.h \
websockets/WebSocket.h \
websockets/WebSocketChannel.h \
websockets/WebSocketChannelClient.h \
websockets/WebSocketHandshake.h \
websockets/WebSocketHandshakeRequest.h \
platform/network/qt/SocketStreamHandlePrivate.h
SOURCES += \
websockets/WebSocket.cpp \
websockets/WebSocketChannel.cpp \
websockets/WebSocketHandshake.cpp \
websockets/ThreadableWebSocketChannel.cpp \
platform/network/SocketStreamErrorBase.cpp \
platform/network/SocketStreamHandleBase.cpp \
platform/network/qt/SocketStreamHandleQt.cpp \
bindings/js/JSWebSocketCustom.cpp \
bindings/js/JSWebSocketConstructor.cpp
SOURCES += \
websockets/WebSocket.cpp \
websockets/WebSocketChannel.cpp \
websockets/WebSocketHandshake.cpp \
websockets/WebSocketHandshakeRequest.cpp \
websockets/ThreadableWebSocketChannel.cpp \
platform/network/SocketStreamErrorBase.cpp \
platform/network/SocketStreamHandleBase.cpp \
platform/network/qt/SocketStreamHandleQt.cpp \
bindings/js/JSWebSocketCustom.cpp \
bindings/js/JSWebSocketConstructor.cpp
contains(DEFINES, ENABLE_WORKERS=1) {
HEADERS += \
websockets/WorkerThreadableWebSocketChannel.h
contains(DEFINES, ENABLE_WORKERS=1) {
SOURCES += \
websockets/WorkerThreadableWebSocketChannel.cpp
}
SOURCES += \
websockets/WorkerThreadableWebSocketChannel.cpp
}
}
contains(DEFINES, ENABLE_3D_CANVAS=1) {
......
......@@ -27049,19 +27049,19 @@
>
</File>
<File
RelativePath="..\rendering\break_lines.cpp"
RelativePath="..\rendering\BidiRun.cpp"
>
</File>
<File
RelativePath="..\rendering\break_lines.h"
RelativePath="..\rendering\BidiRun.h"
>
</File>
<File
RelativePath="..\rendering\BidiRun.cpp"
<File
RelativePath="..\rendering\break_lines.cpp"
>
</File>
<File
RelativePath="..\rendering\BidiRun.h"
RelativePath="..\rendering\break_lines.h"
>
</File>
<File
......@@ -27120,7 +27120,7 @@
RelativePath="..\rendering\InlineFlowBox.h"
>
</File>
<File
<File
RelativePath="..\rendering\InlineIterator.h"
>
</File>
......@@ -34255,6 +34255,22 @@
RelativePath="..\bindings\js\GCController.h"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfile.cpp"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfile.h"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfileNode.cpp"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfileNode.h"
>
</File>
<File
RelativePath="..\bindings\js\JSAbstractWorkerCustom.cpp"
>
......@@ -39943,22 +39959,6 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfile.cpp"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfile.h"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfileNode.cpp"
>
</File>
<File
RelativePath="..\bindings\js\JavaScriptProfileNode.h"
>
</File>
<File
RelativePath="..\bindings\js\ScheduledAction.cpp"
>
......@@ -43056,11 +43056,11 @@
>
</File>
<File
RelativePath="..\inspector\front-end\TextViewer.js"
RelativePath="..\inspector\front-end\textViewer.css"
>
</File>
<File
RelativePath="..\inspector\front-end\textViewer.css"
RelativePath="..\inspector\front-end\TextViewer.js"
>
</File>
<File
......@@ -43488,6 +43488,14 @@
RelativePath="..\websockets\WebSocketHandshake.h"
>
</File>
<File
RelativePath="..\websockets\WebSocketHandshakeRequest.cpp"
>
</File>
<File
RelativePath="..\websockets\WebSocketHandshakeRequest.h"
>
</File>
<File
RelativePath="..\websockets\WorkerThreadableWebSocketChannel.cpp"
>
......
......@@ -1264,6 +1264,8 @@
75793ED30D0CE85B007FC0AC /* DOMMessageEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 75793ED00D0CE85B007FC0AC /* DOMMessageEvent.h */; };
75793ED40D0CE85B007FC0AC /* DOMMessageEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 75793ED10D0CE85B007FC0AC /* DOMMessageEvent.mm */; };
75793ED50D0CE85B007FC0AC /* DOMMessageEventInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 75793ED20D0CE85B007FC0AC /* DOMMessageEventInternal.h */; };
7637C541112E7B74003D6CDC /* WebSocketHandshakeRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7637C540112E7B74003D6CDC /* WebSocketHandshakeRequest.cpp */; };
7637C543112E7B7E003D6CDC /* WebSocketHandshakeRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 7637C542112E7B7E003D6CDC /* WebSocketHandshakeRequest.h */; };
7693BAD2106C2DCA007B0823 /* HaltablePlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 7693BACE106C2DCA007B0823 /* HaltablePlugin.h */; settings = {ATTRIBUTES = (Private, ); }; };
7693BAD3106C2DCA007B0823 /* PluginHalter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7693BACF106C2DCA007B0823 /* PluginHalter.cpp */; };
7693BAD4106C2DCA007B0823 /* PluginHalter.h in Headers */ = {isa = PBXBuildFile; fileRef = 7693BAD0106C2DCA007B0823 /* PluginHalter.h */; };
......@@ -6698,6 +6700,8 @@
75793ED00D0CE85B007FC0AC /* DOMMessageEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DOMMessageEvent.h; sourceTree = "<group>"; };
75793ED10D0CE85B007FC0AC /* DOMMessageEvent.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = DOMMessageEvent.mm; sourceTree = "<group>"; };
75793ED20D0CE85B007FC0AC /* DOMMessageEventInternal.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DOMMessageEventInternal.h; sourceTree = "<group>"; };
7637C540112E7B74003D6CDC /* WebSocketHandshakeRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketHandshakeRequest.cpp; sourceTree = "<group>"; };
7637C542112E7B7E003D6CDC /* WebSocketHandshakeRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketHandshakeRequest.h; sourceTree = "<group>"; };
7693BACE106C2DCA007B0823 /* HaltablePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HaltablePlugin.h; sourceTree = "<group>"; };
7693BACF106C2DCA007B0823 /* PluginHalter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginHalter.cpp; sourceTree = "<group>"; };
7693BAD0106C2DCA007B0823 /* PluginHalter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginHalter.h; sourceTree = "<group>"; };
......@@ -11179,6 +11183,8 @@
518A34BD1026C831001B6896 /* websockets */ = {
isa = PBXGroup;
children = (
7637C542112E7B7E003D6CDC /* WebSocketHandshakeRequest.h */,
7637C540112E7B74003D6CDC /* WebSocketHandshakeRequest.cpp */,
5112247110CFB8C6008099D7 /* ThreadableWebSocketChannel.cpp */,
5112247310CFB8D8008099D7 /* ThreadableWebSocketChannel.h */,
5112247510CFB8E8008099D7 /* ThreadableWebSocketChannelClientWrapper.h */,
......@@ -18598,6 +18604,7 @@
0BC2C7781134A8FC000B2F61 /* CanvasSurface.h in Headers */,
E462A4A1113E71BE004A4220 /* IntPointHash.h in Headers */,
CE172E011136E8CE0062A533 /* ZoomMode.h in Headers */,
7637C543112E7B7E003D6CDC /* WebSocketHandshakeRequest.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -20787,6 +20794,7 @@
E1698264113467F300894115 /* JavaRuntimeObject.cpp in Sources */,
596229781133EFD700DC4CBB /* GeolocationPositionCache.cpp in Sources */,
0BC2C7771134A8FC000B2F61 /* CanvasSurface.cpp in Sources */,
7637C541112E7B74003D6CDC /* WebSocketHandshakeRequest.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -44,6 +44,7 @@
#include "SocketStreamHandle.h"
#include "StringHash.h"
#include "WebSocketChannelClient.h"
#include "WebSocketHandshake.h"
#include <wtf/Deque.h>
#include <wtf/FastMalloc.h>
......
......@@ -130,11 +130,6 @@ bool WebSocketHandshake::secure() const
return m_secure;
}
void WebSocketHandshake::setSecure(bool secure)
{
m_secure = secure;
}
String WebSocketHandshake::clientOrigin() const
{
return m_context->securityOrigin()->toString();
......@@ -158,6 +153,7 @@ String WebSocketHandshake::clientLocation() const
CString WebSocketHandshake::clientHandshakeMessage() const
{
// Keep the following consistent with clientHandshakeRequest().
StringBuilder builder;
builder.append("GET ");
......@@ -167,11 +163,9 @@ CString WebSocketHandshake::clientHandshakeMessage() const
builder.append("Connection: Upgrade\r\n");
builder.append("Host: ");
builder.append(m_url.host().lower());
if (m_url.port()) {
if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) {
builder.append(":");
builder.append(String::number(m_url.port()));
}
if (m_url.port() && ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443))) {
builder.append(":");
builder.append(String::number(m_url.port()));
}
builder.append("\r\n");
builder.append("Origin: ");
......@@ -182,9 +176,8 @@ CString WebSocketHandshake::clientHandshakeMessage() const
builder.append(m_clientProtocol);
builder.append("\r\n");
}
KURL url = httpURLForAuthenticationAndCookies();
// FIXME: set authentication information or cookies for url.
// Set "Authorization: <credentials>" if authentication information exists for url.
if (m_context->isDocument()) {
Document* document = static_cast<Document*>(m_context);
String cookie = cookieRequestHeaderFieldValue(document, url);
......@@ -195,10 +188,28 @@ CString WebSocketHandshake::clientHandshakeMessage() const
}
// Set "Cookie2: <cookie>" if cookies 2 exists for url?
}
builder.append("\r\n");
return builder.toString().utf8();
}
WebSocketHandshakeRequest WebSocketHandshake::clientHandshakeRequest() const
{
// Keep the following consistent with clientHandshakeMessage().
WebSocketHandshakeRequest request(m_url, clientOrigin(), 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.addExtraHeaderField("Cookie", cookie);
// Set "Cookie2: <cookie>" if cookies 2 exists for url?
}
return request;
}
void WebSocketHandshake::reset()
{
m_mode = Incomplete;
......
......@@ -35,6 +35,7 @@
#include "KURL.h"
#include "PlatformString.h"
#include "WebSocketHandshakeRequest.h"
#include <wtf/Noncopyable.h>
namespace WebCore {
......@@ -58,12 +59,12 @@ namespace WebCore {
void setClientProtocol(const String& protocol);
bool secure() const;
void setSecure(bool secure);
String clientOrigin() const;
String clientLocation() const;
CString clientHandshakeMessage() const;
WebSocketHandshakeRequest clientHandshakeRequest() const;
void reset();
......
/*
* Copyright (C) 2010 Google 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.
*/
#include "config.h"
#if ENABLE(WEB_SOCKETS)
#include "WebSocketHandshakeRequest.h"
#include "AtomicString.h"
#include "StringBuilder.h"
#include <utility>
#include <wtf/Assertions.h>
using namespace std;
namespace WebCore {
WebSocketHandshakeRequest::WebSocketHandshakeRequest(const KURL& url, const String& origin, const String& webSocketProtocol)
: m_url(url)
, m_secure(m_url.protocolIs("wss"))
, m_origin(origin)
, m_webSocketProtocol(webSocketProtocol)
{
ASSERT(!origin.isNull());
}
WebSocketHandshakeRequest::~WebSocketHandshakeRequest()
{
}
void WebSocketHandshakeRequest::addExtraHeaderField(const AtomicString& name, const String& value)
{
m_extraHeaderFields.append(HeaderField(name, value));
}
void WebSocketHandshakeRequest::addExtraHeaderField(const char* name, const String& value)
{
m_extraHeaderFields.append(HeaderField(name, value));
}
Vector<WebSocketHandshakeRequest::HeaderField> WebSocketHandshakeRequest::headerFields() const
{
Vector<HeaderField> fields;
fields.append(HeaderField("Upgrade", "WebSocket"));
fields.append(HeaderField("Connection", "Upgrade"));
fields.append(HeaderField("Host", host()));
fields.append(HeaderField("Origin", m_origin));
if (!m_webSocketProtocol.isEmpty())
fields.append(HeaderField("WebSocket-Protocol", m_webSocketProtocol));
fields.append(m_extraHeaderFields);
return fields;
}
String WebSocketHandshakeRequest::host() const
{
StringBuilder builder;
builder.append(m_url.host().lower());
if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) {
builder.append(":");
builder.append(String::number(m_url.port()));
}
return builder.toString();
}
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
/*
* Copyright (C) 2010 Google 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 WebSocketHandshakeRequest_h
#define WebSocketHandshakeRequest_h
#if ENABLE(WEB_SOCKETS)
#include "KURL.h"
#include "PlatformString.h"
#include <wtf/Vector.h>
namespace WebCore {
class AtomicString;
class WebSocketHandshakeRequest {
public:
WebSocketHandshakeRequest(const KURL&, const String& origin, const String& webSocketProtocol);
~WebSocketHandshakeRequest();
// According to current Web Socket protocol specification, four mandatory headers (Upgrade, Connection, Host, and Origin) and
// one optional header (WebSocket-Protocol) should be sent in this order, at the beginning of the handshake request.
// The remaining headers can be set by using the following function.
void addExtraHeaderField(const AtomicString& name, const String& value);
void addExtraHeaderField(const char* name, const String& value);
// Returns the list of header fields including five special ones.
typedef std::pair<AtomicString, String> HeaderField;
Vector<HeaderField> headerFields() const;
private:
String host() const;
KURL m_url;
bool m_secure;
String m_origin;
String m_webSocketProtocol;
Vector<HeaderField> m_extraHeaderFields;
};
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
#endif // WebSocketHandshakeRequest_h
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment