Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPCommon.h"
25 : #include "SPNetworkContext.h"
26 : #include "SPNetworkData.h"
27 : #include "SPNetworkHandle.h"
28 :
29 : #ifdef __MINGW32__
30 : #define CURL_STATICLIB 1
31 : #endif
32 :
33 : #ifdef LINUX
34 : // In linux, MIME types for downloaded files defined in extra FS attributes
35 : #include <sys/types.h>
36 : #include <sys/xattr.h>
37 : #include <sys/stat.h>
38 : #include <fcntl.h>
39 : #endif
40 :
41 : #define SP_NETWORK_LOG(...)
42 : //#define SP_NETWORK_LOG(...) log::format("Network", __VA_ARGS__)
43 :
44 : namespace STAPPLER_VERSIONIZED stappler::network {
45 :
46 : static constexpr auto NetworkUserdataKey = "org.stappler.Network.Handle";
47 :
48 : struct CurlHandle {
49 : public:
50 25 : static CURL *alloc() {
51 25 : ++ s_activeHandles;
52 25 : return curl_easy_init();
53 : }
54 :
55 0 : static void release(CURL *curl) {
56 0 : -- s_activeHandles;
57 0 : curl_easy_cleanup(curl);
58 0 : }
59 :
60 3000 : static CURL * getHandle(bool reuse, memory::pool_t *pool) {
61 3000 : if (reuse) {
62 3000 : if (pool) {
63 0 : void *data = nullptr;
64 0 : memory::pool::userdata_get(&data, NetworkUserdataKey, pool);
65 0 : if (!data) {
66 0 : data = new CurlHandle();
67 0 : memory::pool::userdata_set(data, NetworkUserdataKey, [] (void *obj) {
68 0 : ((CurlHandle *)obj)->~CurlHandle();
69 0 : return 0;
70 : }, pool);
71 : }
72 :
73 0 : return ((CurlHandle *)data)->get();
74 3000 : } else if (!tl_handle) {
75 25 : tl_handle = new CurlHandle();
76 : }
77 3000 : return tl_handle->get();
78 : } else {
79 0 : return CurlHandle::alloc();
80 : }
81 : }
82 :
83 3000 : static void releaseHandle(CURL *curl, bool reuse, bool success, memory::pool_t *pool) {
84 3000 : if (!reuse) {
85 0 : CurlHandle::release(curl);
86 3000 : } else if (pool) {
87 0 : void *data = nullptr;
88 0 : memory::pool::userdata_get(&data, NetworkUserdataKey, pool);
89 0 : if (data) {
90 0 : if (!success) {
91 0 : ((CurlHandle *)data)->invalidate(curl);
92 : } else {
93 0 : ((CurlHandle *)data)->reset();
94 : }
95 : } else {
96 0 : CurlHandle::release(curl);
97 : }
98 3000 : } else if (tl_handle) {
99 3000 : if (!success) {
100 75 : tl_handle->invalidate(curl);
101 : } else {
102 2925 : tl_handle->reset();
103 : }
104 : } else {
105 0 : CurlHandle::release(curl);
106 : }
107 3000 : }
108 :
109 0 : static uint32_t getActiveHandles() {
110 0 : return s_activeHandles;
111 : }
112 :
113 25 : CurlHandle() {
114 25 : _curl = alloc();
115 25 : }
116 :
117 : CurlHandle(CurlHandle &&other) : _curl(other._curl) {
118 : other._curl = nullptr;
119 : }
120 :
121 : CurlHandle & operator = (CurlHandle &&other) {
122 : _curl = other._curl;
123 : other._curl = nullptr;
124 : return *this;
125 : }
126 :
127 : CurlHandle(const CurlHandle &) = delete;
128 : CurlHandle & operator = (const CurlHandle &) = delete;
129 :
130 0 : ~CurlHandle() {
131 0 : if (_curl) {
132 0 : release(_curl);
133 0 : _curl = nullptr;
134 : }
135 0 : }
136 :
137 3000 : CURL *get() { return _curl; }
138 : explicit operator bool () { return _curl != nullptr; }
139 :
140 75 : void invalidate(CURL * curl) {
141 75 : if (_curl == curl) {
142 75 : curl_easy_cleanup(_curl);
143 75 : _curl = curl_easy_init();
144 : }
145 75 : }
146 :
147 2925 : void reset() {
148 2925 : if (_curl) {
149 2925 : curl_easy_reset(_curl);
150 : }
151 2925 : }
152 :
153 : protected:
154 : CURL *_curl = nullptr;
155 : static std::atomic<uint32_t> s_activeHandles;
156 : static thread_local CurlHandle *tl_handle;
157 : };
158 :
159 : std::atomic<uint32_t> CurlHandle::s_activeHandles = 0;
160 : thread_local CurlHandle *CurlHandle::tl_handle = nullptr;
161 :
162 0 : SPUNUSED static CURL *CurlHandle_alloc() {
163 0 : return CurlHandle::alloc();
164 : }
165 :
166 0 : SPUNUSED static void CurlHandle_release(CURL *curl) {
167 0 : CurlHandle::release(curl);
168 0 : }
169 :
170 3000 : SPUNUSED static CURL * CurlHandle_getHandle(bool reuse, memory::pool_t *pool) {
171 3000 : return CurlHandle::getHandle(reuse, pool);
172 : }
173 :
174 3000 : SPUNUSED static void CurlHandle_releaseHandle(CURL *curl, bool reuse, bool success, memory::pool_t *pool) {
175 3000 : CurlHandle::releaseHandle(curl, reuse, success, pool);
176 3000 : }
177 :
178 0 : uint32_t getActiveHandles() {
179 0 : return CurlHandle::getActiveHandles();
180 : }
181 :
182 : #ifdef LINUX
183 :
184 0 : static bool network_setUserAttributes(FILE *file, const StringView &str, Time mtime) {
185 0 : if (int fd = fileno(file)) {
186 0 : auto err = fsetxattr(fd, "user.mime_type", str.data(), str.size(), XATTR_CREATE);
187 0 : if (err != 0) {
188 0 : err = fsetxattr(fd, "user.mime_type", str.data(), str.size(), XATTR_REPLACE);
189 0 : if (err != 0) {
190 0 : std::cout << "Fail to set mime type attribute (" << err << ")\n";
191 0 : return false;
192 : }
193 : }
194 :
195 0 : if (mtime != nullptr) {
196 : struct timespec times[2];
197 0 : times[0].tv_nsec = UTIME_OMIT;
198 :
199 0 : times[1].tv_sec = time_t(mtime.sec());
200 0 : times[1].tv_nsec = (mtime.toMicroseconds() - Time::seconds(mtime.sec()).toMicroseconds()) * 1000;
201 0 : futimens(fd, times);
202 : }
203 :
204 0 : return true;
205 : }
206 0 : return false;
207 : }
208 :
209 : template <typename Interface>
210 0 : static auto network_getUserMime(const StringView &filename) -> typename Interface::StringType {
211 0 : auto path = filepath::absolute<Interface>(filename);
212 :
213 0 : char buf[1_KiB] = { 0 };
214 0 : auto vallen = getxattr(path.data(), "user.mime_type", buf, 1_KiB);
215 0 : if (vallen == -1) {
216 0 : return typename Interface::StringType();
217 : }
218 :
219 0 : return StringView(buf, vallen).str<Interface>();
220 0 : }
221 :
222 : #endif
223 :
224 :
225 : }
226 :
227 : #include "SPNetworkCABundle.cc"
228 : #include "SPNetworkSetup.cc"
229 : #include "SPNetworkData.cc"
230 : #include "SPNetworkHandle.cc"
231 :
232 : //#include "SPNetworkMultiHandle.cc"
|