1 | /* |
2 | * Copyright (C) 2012 Igalia S.L. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library 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 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library 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 | #include "WebKitSoupRequestInputStream.h" |
22 | |
23 | #include <wtf/MainThread.h> |
24 | #include <wtf/RunLoop.h> |
25 | #include <wtf/glib/GRefPtr.h> |
26 | #include <wtf/glib/GUniquePtr.h> |
27 | |
28 | struct AsyncReadData { |
29 | AsyncReadData(GRefPtr<GTask>&& task, void* buffer, gsize count) |
30 | : task(WTFMove(task)) |
31 | , buffer(buffer) |
32 | , count(count) |
33 | { |
34 | } |
35 | |
36 | GRefPtr<GTask> task; |
37 | void* buffer; |
38 | size_t count; |
39 | }; |
40 | |
41 | struct _WebKitSoupRequestInputStreamPrivate { |
42 | uint64_t contentLength; |
43 | uint64_t bytesReceived; |
44 | uint64_t bytesRead; |
45 | |
46 | GUniquePtr<GError> error; |
47 | |
48 | std::unique_ptr<AsyncReadData> pendingAsyncRead; |
49 | }; |
50 | |
51 | G_DEFINE_TYPE(WebKitSoupRequestInputStream, webkit_soup_request_input_stream, G_TYPE_MEMORY_INPUT_STREAM) |
52 | |
53 | static void webkitSoupRequestInputStreamReadAsyncResultComplete(GTask* task, void* buffer, gsize count) |
54 | { |
55 | WebKitSoupRequestInputStream* stream = WEBKIT_SOUP_REQUEST_INPUT_STREAM(g_task_get_source_object(task)); |
56 | GError* error = nullptr; |
57 | gssize bytesRead = G_INPUT_STREAM_GET_CLASS(stream)->read_fn(G_INPUT_STREAM(stream), buffer, count, g_task_get_cancellable(task), &error); |
58 | if (!error) { |
59 | stream->priv->bytesRead += bytesRead; |
60 | g_task_return_int(task, bytesRead); |
61 | } else |
62 | g_task_return_error(task, error); |
63 | } |
64 | |
65 | static void webkitSoupRequestInputStreamPendingReadAsyncComplete(WebKitSoupRequestInputStream* stream) |
66 | { |
67 | if (auto data = WTFMove(stream->priv->pendingAsyncRead)) |
68 | webkitSoupRequestInputStreamReadAsyncResultComplete(data->task.get(), data->buffer, data->count); |
69 | } |
70 | |
71 | static bool webkitSoupRequestInputStreamHasDataToRead(WebKitSoupRequestInputStream* stream) |
72 | { |
73 | return stream->priv->bytesRead < stream->priv->bytesReceived; |
74 | } |
75 | |
76 | static bool webkitSoupRequestInputStreamIsWaitingForData(WebKitSoupRequestInputStream* stream) |
77 | { |
78 | return !stream->priv->contentLength || stream->priv->bytesReceived < stream->priv->contentLength; |
79 | } |
80 | |
81 | static void webkitSoupRequestInputStreamReadAsync(GInputStream* inputStream, void* buffer, gsize count, int /*priority*/, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
82 | { |
83 | ASSERT(RunLoop::isMain()); |
84 | WebKitSoupRequestInputStream* stream = WEBKIT_SOUP_REQUEST_INPUT_STREAM(inputStream); |
85 | GRefPtr<GTask> task = adoptGRef(g_task_new(stream, cancellable, callback, userData)); |
86 | |
87 | if (!webkitSoupRequestInputStreamHasDataToRead(stream) && !webkitSoupRequestInputStreamIsWaitingForData(stream)) { |
88 | g_task_return_int(task.get(), 0); |
89 | return; |
90 | } |
91 | |
92 | if (stream->priv->error.get()) { |
93 | g_task_return_error(task.get(), stream->priv->error.release()); |
94 | return; |
95 | } |
96 | |
97 | if (webkitSoupRequestInputStreamHasDataToRead(stream)) { |
98 | webkitSoupRequestInputStreamReadAsyncResultComplete(task.get(), buffer, count); |
99 | return; |
100 | } |
101 | |
102 | stream->priv->pendingAsyncRead = std::make_unique<AsyncReadData>(WTFMove(task), buffer, count); |
103 | } |
104 | |
105 | static gssize webkitSoupRequestInputStreamReadFinish(GInputStream* inputStream, GAsyncResult* result, GError** error) |
106 | { |
107 | g_return_val_if_fail(g_task_is_valid(result, inputStream), 0); |
108 | |
109 | return g_task_propagate_int(G_TASK(result), error); |
110 | } |
111 | |
112 | static void webkitSoupRequestInputStreamFinalize(GObject* object) |
113 | { |
114 | WEBKIT_SOUP_REQUEST_INPUT_STREAM(object)->priv->~WebKitSoupRequestInputStreamPrivate(); |
115 | G_OBJECT_CLASS(webkit_soup_request_input_stream_parent_class)->finalize(object); |
116 | } |
117 | |
118 | static void webkit_soup_request_input_stream_init(WebKitSoupRequestInputStream* stream) |
119 | { |
120 | WebKitSoupRequestInputStreamPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(stream, WEBKIT_TYPE_SOUP_REQUEST_INPUT_STREAM, WebKitSoupRequestInputStreamPrivate); |
121 | stream->priv = priv; |
122 | new (priv) WebKitSoupRequestInputStreamPrivate(); |
123 | } |
124 | |
125 | static void webkit_soup_request_input_stream_class_init(WebKitSoupRequestInputStreamClass* requestStreamClass) |
126 | { |
127 | GObjectClass* gObjectClass = G_OBJECT_CLASS(requestStreamClass); |
128 | gObjectClass->finalize = webkitSoupRequestInputStreamFinalize; |
129 | |
130 | GInputStreamClass* inputStreamClass = G_INPUT_STREAM_CLASS(requestStreamClass); |
131 | inputStreamClass->read_async = webkitSoupRequestInputStreamReadAsync; |
132 | inputStreamClass->read_finish = webkitSoupRequestInputStreamReadFinish; |
133 | |
134 | g_type_class_add_private(requestStreamClass, sizeof(WebKitSoupRequestInputStreamPrivate)); |
135 | } |
136 | |
137 | GInputStream* webkitSoupRequestInputStreamNew(uint64_t contentLength) |
138 | { |
139 | WebKitSoupRequestInputStream* stream = WEBKIT_SOUP_REQUEST_INPUT_STREAM(g_object_new(WEBKIT_TYPE_SOUP_REQUEST_INPUT_STREAM, NULL)); |
140 | stream->priv->contentLength = contentLength; |
141 | return G_INPUT_STREAM(stream); |
142 | } |
143 | |
144 | void webkitSoupRequestInputStreamAddData(WebKitSoupRequestInputStream* stream, const void* data, size_t dataLength) |
145 | { |
146 | ASSERT(RunLoop::isMain()); |
147 | |
148 | if (webkitSoupRequestInputStreamFinished(stream)) |
149 | return; |
150 | |
151 | if (dataLength) { |
152 | // Truncate the dataLength to the contentLength if it's known. |
153 | if (stream->priv->contentLength && stream->priv->bytesReceived + dataLength > stream->priv->contentLength) |
154 | dataLength = stream->priv->contentLength - stream->priv->bytesReceived; |
155 | stream->priv->bytesReceived += dataLength; |
156 | g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(stream), g_memdup(data, dataLength), dataLength, g_free); |
157 | } else { |
158 | // We have received all the data, set contentLength to bytesReceived to indicate we have finished. |
159 | stream->priv->contentLength = stream->priv->bytesReceived; |
160 | // If there's a pending read to complete, read_fn will return 0 because we haven't added more data to the |
161 | // memory input stream. And if there isn't a pending read, the next call to read_async will return 0 too, because |
162 | // webkitSoupRequestInputStreamFinished() is now TRUE. |
163 | } |
164 | |
165 | webkitSoupRequestInputStreamPendingReadAsyncComplete(stream); |
166 | } |
167 | |
168 | void webkitSoupRequestInputStreamDidFailWithError(WebKitSoupRequestInputStream* stream, const WebCore::ResourceError& resourceError) |
169 | { |
170 | GUniquePtr<GError> error(g_error_new(g_quark_from_string(resourceError.domain().utf8().data()), resourceError.errorCode(), "%s" , resourceError.localizedDescription().utf8().data())); |
171 | if (auto data = WTFMove(stream->priv->pendingAsyncRead)) |
172 | g_task_return_error(data->task.get(), error.release()); |
173 | else { |
174 | stream->priv->contentLength = stream->priv->bytesReceived; |
175 | stream->priv->error = WTFMove(error); |
176 | } |
177 | } |
178 | |
179 | bool webkitSoupRequestInputStreamFinished(WebKitSoupRequestInputStream* stream) |
180 | { |
181 | return !webkitSoupRequestInputStreamIsWaitingForData(stream); |
182 | } |
183 | |