b5de57b3f1f4d87fedf32e74094d50636548ed2e
[WebKit-https.git] / Source / WebCore / platform / network / PingHandle.h
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #include "ResourceHandle.h"
29 #include "ResourceHandleClient.h"
30 #include "Timer.h"
31 #include <wtf/CompletionHandler.h>
32
33 namespace WebCore {
34
35 // This class triggers asynchronous loads independent of the networking context staying alive (i.e., auditing pingbacks).
36 // The object just needs to live long enough to ensure the message was actually sent.
37 // As soon as any callback is received from the ResourceHandle, this class will cancel the load and delete itself.
38
39 class PingHandle final : private ResourceHandleClient {
40     WTF_MAKE_NONCOPYABLE(PingHandle); WTF_MAKE_FAST_ALLOCATED;
41 public:
42     enum class UsesAsyncCallbacks {
43         Yes,
44         No,
45     };
46     
47     PingHandle(NetworkingContext* networkingContext, const ResourceRequest& request, bool shouldUseCredentialStorage, UsesAsyncCallbacks useAsyncCallbacks, bool shouldFollowRedirects, WTF::CompletionHandler<void(const ResourceError&)>&& completionHandler)
48         : m_currentRequest(request)
49         , m_timeoutTimer(*this, &PingHandle::timeoutTimerFired)
50         , m_shouldUseCredentialStorage(shouldUseCredentialStorage)
51         , m_shouldFollowRedirects(shouldFollowRedirects)
52         , m_usesAsyncCallbacks(useAsyncCallbacks)
53         , m_completionHandler(WTFMove(completionHandler))
54     {
55         m_handle = ResourceHandle::create(networkingContext, request, this, false, false);
56
57         // If the server never responds, this object will hang around forever.
58         // Set a very generous timeout, just in case.
59         m_timeoutTimer.startOneShot(60000_s);
60     }
61
62 private:
63     ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&) final
64     {
65         return m_shouldFollowRedirects ? request : ResourceRequest();
66     }
67     void willSendRequestAsync(ResourceHandle* handle, ResourceRequest&& request, ResourceResponse&&) final
68     {
69         m_currentRequest = WTFMove(request);
70         if (m_shouldFollowRedirects) {
71             handle->continueWillSendRequest(ResourceRequest { m_currentRequest });
72             return;
73         }
74         pingLoadComplete(ResourceError { String(), 0, m_currentRequest.url(), ASCIILiteral("Not allowed to follow redirects"), ResourceError::Type::AccessControl });
75     }
76     void didReceiveResponse(ResourceHandle*, ResourceResponse&&) final { pingLoadComplete(); }
77     void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int) final { pingLoadComplete(); }
78     void didFinishLoading(ResourceHandle*) final { pingLoadComplete(); }
79     void didFail(ResourceHandle*, const ResourceError& error) final { pingLoadComplete(error); }
80     bool shouldUseCredentialStorage(ResourceHandle*) final { return m_shouldUseCredentialStorage; }
81     bool usesAsyncCallbacks() final { return m_usesAsyncCallbacks == UsesAsyncCallbacks::Yes; }
82     void timeoutTimerFired() { pingLoadComplete(ResourceError { String(), 0, m_currentRequest.url(), ASCIILiteral("Load timed out"), ResourceError::Type::Timeout }); }
83
84     void pingLoadComplete(const ResourceError& error = { })
85     {
86         if (auto completionHandler = std::exchange(m_completionHandler, nullptr))
87             completionHandler(error);
88         delete this;
89     }
90
91     virtual ~PingHandle()
92     {
93         ASSERT(!m_completionHandler);
94         if (m_handle) {
95             ASSERT(m_handle->client() == this);
96             m_handle->clearClient();
97             m_handle->cancel();
98         }
99     }
100
101     RefPtr<ResourceHandle> m_handle;
102     ResourceRequest m_currentRequest;
103     Timer m_timeoutTimer;
104     bool m_shouldUseCredentialStorage;
105     bool m_shouldFollowRedirects;
106     UsesAsyncCallbacks m_usesAsyncCallbacks;
107     WTF::CompletionHandler<void(const ResourceError&)> m_completionHandler;
108 };
109
110 } // namespace WebCore