diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-11-24 22:11:11 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-11-24 22:11:11 +0200 |
commit | 5268fb9f7e1bca4b0fc496ff115c253aea724e49 (patch) | |
tree | 8023f45f3538f7966c2f75674a4b14fb4778e79c /src | |
parent | 9421f64ee86e6aec26e05a45bcfc65edd895a8a8 (diff) |
Fixed threading issues and data races
The most serious problem was that GmRequest's response body was being accessed while the TlsRequest thread was modifying it.
Now the response must always be locked before accessing elsewhere.
There were also inefficient data updates in the media players.
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 14 | ||||
-rw-r--r-- | src/audio/player.c | 2 | ||||
-rw-r--r-- | src/gmdocument.c | 2 | ||||
-rw-r--r-- | src/gmrequest.c | 233 | ||||
-rw-r--r-- | src/gmrequest.h | 5 | ||||
-rw-r--r-- | src/media.c | 10 | ||||
-rw-r--r-- | src/media.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 51 |
8 files changed, 176 insertions, 143 deletions
@@ -99,7 +99,7 @@ struct Impl_App { | |||
99 | uint32_t lastTickerTime; | 99 | uint32_t lastTickerTime; |
100 | uint32_t elapsedSinceLastTicker; | 100 | uint32_t elapsedSinceLastTicker; |
101 | iBool running; | 101 | iBool running; |
102 | iBool pendingRefresh; | 102 | iAtomicInt pendingRefresh; |
103 | int tabEnum; | 103 | int tabEnum; |
104 | iStringList *launchCommands; | 104 | iStringList *launchCommands; |
105 | iBool isFinishedLaunching; | 105 | iBool isFinishedLaunching; |
@@ -355,7 +355,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
355 | setCStr_String(&d->prefs.downloadDir, downloadDir_App_); | 355 | setCStr_String(&d->prefs.downloadDir, downloadDir_App_); |
356 | d->running = iFalse; | 356 | d->running = iFalse; |
357 | d->window = NULL; | 357 | d->window = NULL; |
358 | d->pendingRefresh = iFalse; | 358 | set_Atomic(&d->pendingRefresh, iFalse); |
359 | d->certs = new_GmCerts(dataDir_App_); | 359 | d->certs = new_GmCerts(dataDir_App_); |
360 | d->visited = new_Visited(); | 360 | d->visited = new_Visited(); |
361 | d->bookmarks = new_Bookmarks(); | 361 | d->bookmarks = new_Bookmarks(); |
@@ -484,7 +484,7 @@ const iString *debugInfo_App(void) { | |||
484 | } | 484 | } |
485 | 485 | ||
486 | iLocalDef iBool isWaitingAllowed_App_(const iApp *d) { | 486 | iLocalDef iBool isWaitingAllowed_App_(const iApp *d) { |
487 | return !d->pendingRefresh && isEmpty_SortedArray(&d->tickers); | 487 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); |
488 | } | 488 | } |
489 | 489 | ||
490 | void processEvents_App(enum iAppEventMode eventMode) { | 490 | void processEvents_App(enum iAppEventMode eventMode) { |
@@ -583,11 +583,11 @@ void refresh_App(void) { | |||
583 | iApp *d = &app_; | 583 | iApp *d = &app_; |
584 | destroyPending_Widget(); | 584 | destroyPending_Widget(); |
585 | draw_Window(d->window); | 585 | draw_Window(d->window); |
586 | d->pendingRefresh = iFalse; | 586 | set_Atomic(&d->pendingRefresh, iFalse); |
587 | } | 587 | } |
588 | 588 | ||
589 | iBool isRefreshPending_App(void) { | 589 | iBool isRefreshPending_App(void) { |
590 | return app_.pendingRefresh; | 590 | return value_Atomic(&app_.pendingRefresh); |
591 | } | 591 | } |
592 | 592 | ||
593 | uint32_t elapsedSinceLastTicker_App(void) { | 593 | uint32_t elapsedSinceLastTicker_App(void) { |
@@ -642,8 +642,8 @@ int run_App(int argc, char **argv) { | |||
642 | 642 | ||
643 | void postRefresh_App(void) { | 643 | void postRefresh_App(void) { |
644 | iApp *d = &app_; | 644 | iApp *d = &app_; |
645 | if (!d->pendingRefresh) { | 645 | const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue); |
646 | d->pendingRefresh = iTrue; | 646 | if (!wasPending) { |
647 | SDL_Event ev; | 647 | SDL_Event ev; |
648 | ev.user.type = SDL_USEREVENT; | 648 | ev.user.type = SDL_USEREVENT; |
649 | ev.user.code = refresh_UserEventCode; | 649 | ev.user.code = refresh_UserEventCode; |
diff --git a/src/audio/player.c b/src/audio/player.c index 77c23104..1c8538b4 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -667,7 +667,7 @@ void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock * | |||
667 | const size_t newSize = size_Block(data); | 667 | const size_t newSize = size_Block(data); |
668 | iAssert(newSize >= oldSize); | 668 | iAssert(newSize >= oldSize); |
669 | /* The old parts cannot have changed. */ | 669 | /* The old parts cannot have changed. */ |
670 | iAssert(memcmp(constData_Block(&input->data), constData_Block(data), oldSize) == 0); | 670 | // iAssert(memcmp(constData_Block(&input->data), constData_Block(data), oldSize) == 0); |
671 | appendData_Block(&input->data, constBegin_Block(data) + oldSize, newSize - oldSize); | 671 | appendData_Block(&input->data, constBegin_Block(data) + oldSize, newSize - oldSize); |
672 | input->isComplete = iFalse; | 672 | input->isComplete = iFalse; |
673 | break; | 673 | break; |
diff --git a/src/gmdocument.c b/src/gmdocument.c index c8fc9869..50c79459 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -535,7 +535,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
535 | } | 535 | } |
536 | /* Flag the end of line, too. */ | 536 | /* Flag the end of line, too. */ |
537 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; | 537 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; |
538 | /* Image content. */ | 538 | /* Image or audio content. */ |
539 | if (type == link_GmLineType) { | 539 | if (type == link_GmLineType) { |
540 | const iMediaId imageId = findLinkImage_Media(d->media, run.linkId); | 540 | const iMediaId imageId = findLinkImage_Media(d->media, run.linkId); |
541 | const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0; | 541 | const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0; |
diff --git a/src/gmrequest.c b/src/gmrequest.c index 15782e01..14f77405 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c | |||
@@ -123,13 +123,15 @@ enum iGmRequestState { | |||
123 | 123 | ||
124 | struct Impl_GmRequest { | 124 | struct Impl_GmRequest { |
125 | iObject object; | 125 | iObject object; |
126 | iMutex mutex; | 126 | iMutex * mtx; |
127 | iGmCerts * certs; /* not owned */ | 127 | iGmCerts * certs; /* not owned */ |
128 | enum iGmRequestState state; | 128 | enum iGmRequestState state; |
129 | iString url; | 129 | iString url; |
130 | iTlsRequest * req; | 130 | iTlsRequest * req; |
131 | iGopher gopher; | 131 | iGopher gopher; |
132 | iGmResponse resp; | 132 | iGmResponse * resp; |
133 | iBool respLocked; | ||
134 | iAtomicInt allowUpdate; | ||
133 | iAudience * updated; | 135 | iAudience * updated; |
134 | iAudience * finished; | 136 | iAudience * finished; |
135 | }; | 137 | }; |
@@ -140,85 +142,77 @@ iDefineAudienceGetter(GmRequest, finished) | |||
140 | 142 | ||
141 | static void checkServerCertificate_GmRequest_(iGmRequest *d) { | 143 | static void checkServerCertificate_GmRequest_(iGmRequest *d) { |
142 | const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); | 144 | const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); |
143 | d->resp.certFlags = 0; | 145 | iGmResponse *resp = d->resp; |
146 | resp->certFlags = 0; | ||
144 | if (cert) { | 147 | if (cert) { |
145 | const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req))); | 148 | const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req))); |
146 | d->resp.certFlags |= available_GmCertFlag; | 149 | resp->certFlags |= available_GmCertFlag; |
147 | set_Block(&d->resp.certFingerprint, collect_Block(fingerprint_TlsCertificate(cert))); | 150 | set_Block(&resp->certFingerprint, collect_Block(fingerprint_TlsCertificate(cert))); |
148 | d->resp.certFlags |= haveFingerprint_GmCertFlag; | 151 | resp->certFlags |= haveFingerprint_GmCertFlag; |
149 | if (!isExpired_TlsCertificate(cert)) { | 152 | if (!isExpired_TlsCertificate(cert)) { |
150 | d->resp.certFlags |= timeVerified_GmCertFlag; | 153 | resp->certFlags |= timeVerified_GmCertFlag; |
151 | } | 154 | } |
152 | /* TODO: Check for IP too (see below), because it may be specified in the SAN. */ | ||
153 | #if 0 | ||
154 | iString *ip = toStringFlags_Address(address_TlsRequest(d->req), noPort_SocketStringFlag, 0); | ||
155 | if (verifyIp_TlsCertificate(cert, ip)) { | ||
156 | printf("[GmRequest] IP address %s matches!\n", cstr_String(ip)); | ||
157 | } | ||
158 | else { | ||
159 | printf("[GmRequest] IP address %s not matched\n", cstr_String(ip)); | ||
160 | } | ||
161 | delete_String(ip); | ||
162 | #endif | ||
163 | if (verifyDomain_TlsCertificate(cert, domain)) { | 155 | if (verifyDomain_TlsCertificate(cert, domain)) { |
164 | d->resp.certFlags |= domainVerified_GmCertFlag; | 156 | resp->certFlags |= domainVerified_GmCertFlag; |
165 | } | 157 | } |
166 | if (checkTrust_GmCerts(d->certs, domain, cert)) { | 158 | if (checkTrust_GmCerts(d->certs, domain, cert)) { |
167 | d->resp.certFlags |= trusted_GmCertFlag; | 159 | resp->certFlags |= trusted_GmCertFlag; |
168 | } | 160 | } |
169 | validUntil_TlsCertificate(cert, &d->resp.certValidUntil); | 161 | validUntil_TlsCertificate(cert, &resp->certValidUntil); |
170 | set_String(&d->resp.certSubject, collect_String(subject_TlsCertificate(cert))); | 162 | set_String(&resp->certSubject, collect_String(subject_TlsCertificate(cert))); |
171 | } | 163 | } |
172 | } | 164 | } |
173 | 165 | ||
174 | static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { | 166 | static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { |
175 | iBool notifyUpdate = iFalse; | 167 | iBool notifyUpdate = iFalse; |
176 | iBool notifyDone = iFalse; | 168 | iBool notifyDone = iFalse; |
177 | lock_Mutex(&d->mutex); | 169 | lock_Mutex(d->mtx); |
170 | iGmResponse *resp =d->resp; | ||
178 | iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ | 171 | iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ |
179 | iBlock *data = readAll_TlsRequest(req); | 172 | iBlock *data = readAll_TlsRequest(req); |
180 | if (d->state == receivingHeader_GmRequestState) { | 173 | if (d->state == receivingHeader_GmRequestState) { |
181 | appendCStrN_String(&d->resp.meta, constData_Block(data), size_Block(data)); | 174 | appendCStrN_String(&resp->meta, constData_Block(data), size_Block(data)); |
182 | /* Check if the header line is complete. */ | 175 | /* Check if the header line is complete. */ |
183 | size_t endPos = indexOfCStr_String(&d->resp.meta, "\r\n"); | 176 | size_t endPos = indexOfCStr_String(&resp->meta, "\r\n"); |
184 | if (endPos != iInvalidPos) { | 177 | if (endPos != iInvalidPos) { |
185 | /* Move remainder to the body. */ | 178 | /* Move remainder to the body. */ |
186 | setData_Block(&d->resp.body, | 179 | setData_Block(&resp->body, |
187 | constBegin_String(&d->resp.meta) + endPos + 2, | 180 | constBegin_String(&resp->meta) + endPos + 2, |
188 | size_String(&d->resp.meta) - endPos - 2); | 181 | size_String(&resp->meta) - endPos - 2); |
189 | remove_Block(&d->resp.meta.chars, endPos, iInvalidSize); | 182 | remove_Block(&resp->meta.chars, endPos, iInvalidSize); |
190 | /* parse and remove the code */ | 183 | /* parse and remove the code */ |
191 | if (size_String(&d->resp.meta) < 3) { | 184 | if (size_String(&resp->meta) < 3) { |
192 | clear_String(&d->resp.meta); | 185 | clear_String(&resp->meta); |
193 | d->resp.statusCode = invalidHeader_GmStatusCode; | 186 | resp->statusCode = invalidHeader_GmStatusCode; |
194 | d->state = finished_GmRequestState; | 187 | d->state = finished_GmRequestState; |
195 | notifyDone = iTrue; | 188 | notifyDone = iTrue; |
196 | } | 189 | } |
197 | const int code = toInt_String(&d->resp.meta); | 190 | const int code = toInt_String(&resp->meta); |
198 | if (code == 0) { | 191 | if (code == 0) { |
199 | clear_String(&d->resp.meta); | 192 | clear_String(&resp->meta); |
200 | d->resp.statusCode = invalidHeader_GmStatusCode; | 193 | resp->statusCode = invalidHeader_GmStatusCode; |
201 | d->state = finished_GmRequestState; | 194 | d->state = finished_GmRequestState; |
202 | notifyDone = iTrue; | 195 | notifyDone = iTrue; |
203 | } | 196 | } |
204 | remove_Block(&d->resp.meta.chars, 0, 3); /* just the meta */ | 197 | remove_Block(&resp->meta.chars, 0, 3); /* just the meta */ |
205 | if (code == success_GmStatusCode && isEmpty_String(&d->resp.meta)) { | 198 | if (code == success_GmStatusCode && isEmpty_String(&resp->meta)) { |
206 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); /* default */ | 199 | setCStr_String(&resp->meta, "text/gemini; charset=utf-8"); /* default */ |
207 | } | 200 | } |
208 | d->resp.statusCode = code; | 201 | resp->statusCode = code; |
209 | d->state = receivingBody_GmRequestState; | 202 | d->state = receivingBody_GmRequestState; |
210 | checkServerCertificate_GmRequest_(d); | 203 | checkServerCertificate_GmRequest_(d); |
211 | notifyUpdate = iTrue; | 204 | notifyUpdate = iTrue; |
212 | } | 205 | } |
213 | } | 206 | } |
214 | else if (d->state == receivingBody_GmRequestState) { | 207 | else if (d->state == receivingBody_GmRequestState) { |
215 | append_Block(&d->resp.body, data); | 208 | append_Block(&resp->body, data); |
216 | notifyUpdate = iTrue; | 209 | notifyUpdate = iTrue; |
217 | } | 210 | } |
218 | initCurrent_Time(&d->resp.when); | 211 | initCurrent_Time(&resp->when); |
219 | delete_Block(data); | 212 | delete_Block(data); |
220 | unlock_Mutex(&d->mutex); | 213 | unlock_Mutex(d->mtx); |
221 | if (notifyUpdate) { | 214 | const iBool allowed = exchange_Atomic(&d->allowUpdate, iFalse); |
215 | if (notifyUpdate && allowed) { | ||
222 | iNotifyAudience(d, updated, GmRequestUpdated); | 216 | iNotifyAudience(d, updated, GmRequestUpdated); |
223 | } | 217 | } |
224 | if (notifyDone) { | 218 | if (notifyDone) { |
@@ -228,21 +222,21 @@ static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { | |||
228 | 222 | ||
229 | static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) { | 223 | static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) { |
230 | iAssert(req == d->req); | 224 | iAssert(req == d->req); |
231 | lock_Mutex(&d->mutex); | 225 | lock_Mutex(d->mtx); |
232 | /* There shouldn't be anything left to read. */ { | 226 | /* There shouldn't be anything left to read. */ { |
233 | iBlock *data = readAll_TlsRequest(req); | 227 | iBlock *data = readAll_TlsRequest(req); |
234 | iAssert(isEmpty_Block(data)); | 228 | iAssert(isEmpty_Block(data)); |
235 | delete_Block(data); | 229 | delete_Block(data); |
236 | initCurrent_Time(&d->resp.when); | 230 | initCurrent_Time(&d->resp->when); |
237 | } | 231 | } |
238 | d->state = (status_TlsRequest(req) == error_TlsRequestStatus ? failure_GmRequestState | 232 | d->state = (status_TlsRequest(req) == error_TlsRequestStatus ? failure_GmRequestState |
239 | : finished_GmRequestState); | 233 | : finished_GmRequestState); |
240 | if (d->state == failure_GmRequestState) { | 234 | if (d->state == failure_GmRequestState) { |
241 | d->resp.statusCode = tlsFailure_GmStatusCode; | 235 | d->resp->statusCode = tlsFailure_GmStatusCode; |
242 | set_String(&d->resp.meta, errorMessage_TlsRequest(req)); | 236 | set_String(&d->resp->meta, errorMessage_TlsRequest(req)); |
243 | } | 237 | } |
244 | checkServerCertificate_GmRequest_(d); | 238 | checkServerCertificate_GmRequest_(d); |
245 | unlock_Mutex(&d->mutex); | 239 | unlock_Mutex(d->mtx); |
246 | iNotifyAudience(d, finished, GmRequestFinished); | 240 | iNotifyAudience(d, finished, GmRequestFinished); |
247 | } | 241 | } |
248 | 242 | ||
@@ -349,14 +343,14 @@ static const iBlock *replaceVariables_(const iBlock *block) { | |||
349 | 343 | ||
350 | static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) { | 344 | static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) { |
351 | iBool notifyUpdate = iFalse; | 345 | iBool notifyUpdate = iFalse; |
352 | lock_Mutex(&d->mutex); | 346 | lock_Mutex(d->mtx); |
353 | d->resp.statusCode = success_GmStatusCode; | 347 | d->resp->statusCode = success_GmStatusCode; |
354 | iBlock *data = readAll_Socket(socket); | 348 | iBlock *data = readAll_Socket(socket); |
355 | if (!isEmpty_Block(data)) { | 349 | if (!isEmpty_Block(data)) { |
356 | processResponse_Gopher(&d->gopher, data); | 350 | processResponse_Gopher(&d->gopher, data); |
357 | } | 351 | } |
358 | delete_Block(data); | 352 | delete_Block(data); |
359 | unlock_Mutex(&d->mutex); | 353 | unlock_Mutex(d->mtx); |
360 | if (notifyUpdate) { | 354 | if (notifyUpdate) { |
361 | iNotifyAudience(d, updated, GmRequestUpdated); | 355 | iNotifyAudience(d, updated, GmRequestUpdated); |
362 | } | 356 | } |
@@ -365,12 +359,12 @@ static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) { | |||
365 | static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) { | 359 | static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) { |
366 | iUnused(socket); | 360 | iUnused(socket); |
367 | iBool notify = iFalse; | 361 | iBool notify = iFalse; |
368 | lock_Mutex(&d->mutex); | 362 | lock_Mutex(d->mtx); |
369 | if (d->state != failure_GmRequestState) { | 363 | if (d->state != failure_GmRequestState) { |
370 | d->state = finished_GmRequestState; | 364 | d->state = finished_GmRequestState; |
371 | notify = iTrue; | 365 | notify = iTrue; |
372 | } | 366 | } |
373 | unlock_Mutex(&d->mutex); | 367 | unlock_Mutex(d->mtx); |
374 | if (notify) { | 368 | if (notify) { |
375 | iNotifyAudience(d, finished, GmRequestFinished); | 369 | iNotifyAudience(d, finished, GmRequestFinished); |
376 | } | 370 | } |
@@ -378,12 +372,12 @@ static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) { | |||
378 | 372 | ||
379 | static void gopherError_GmRequest_(iGmRequest *d, iSocket *socket, int error, const char *msg) { | 373 | static void gopherError_GmRequest_(iGmRequest *d, iSocket *socket, int error, const char *msg) { |
380 | iUnused(socket); | 374 | iUnused(socket); |
381 | lock_Mutex(&d->mutex); | 375 | lock_Mutex(d->mtx); |
382 | d->state = failure_GmRequestState; | 376 | d->state = failure_GmRequestState; |
383 | d->resp.statusCode = tlsFailure_GmStatusCode; | 377 | d->resp->statusCode = tlsFailure_GmStatusCode; |
384 | format_String(&d->resp.meta, "%s (errno %d)", msg, error); | 378 | format_String(&d->resp->meta, "%s (errno %d)", msg, error); |
385 | clear_Block(&d->resp.body); | 379 | clear_Block(&d->resp->body); |
386 | unlock_Mutex(&d->mutex); | 380 | unlock_Mutex(d->mtx); |
387 | iNotifyAudience(d, finished, GmRequestFinished); | 381 | iNotifyAudience(d, finished, GmRequestFinished); |
388 | } | 382 | } |
389 | 383 | ||
@@ -392,8 +386,9 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
392 | port = 70; /* default port */ | 386 | port = 70; /* default port */ |
393 | } | 387 | } |
394 | clear_Block(&d->gopher.source); | 388 | clear_Block(&d->gopher.source); |
395 | d->gopher.meta = &d->resp.meta; | 389 | iGmResponse *resp = d->resp; |
396 | d->gopher.output = &d->resp.body; | 390 | d->gopher.meta = &resp->meta; |
391 | d->gopher.output = &resp->body; | ||
397 | d->state = receivingBody_GmRequestState; | 392 | d->state = receivingBody_GmRequestState; |
398 | d->gopher.socket = new_Socket(cstr_String(host), port); | 393 | d->gopher.socket = new_Socket(cstr_String(host), port); |
399 | iConnect(Socket, d->gopher.socket, readyRead, d, gopherRead_GmRequest_); | 394 | iConnect(Socket, d->gopher.socket, readyRead, d, gopherRead_GmRequest_); |
@@ -401,8 +396,8 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
401 | iConnect(Socket, d->gopher.socket, error, d, gopherError_GmRequest_); | 396 | iConnect(Socket, d->gopher.socket, error, d, gopherError_GmRequest_); |
402 | open_Gopher(&d->gopher, &d->url); | 397 | open_Gopher(&d->gopher, &d->url); |
403 | if (d->gopher.needQueryArgs) { | 398 | if (d->gopher.needQueryArgs) { |
404 | d->resp.statusCode = input_GmStatusCode; | 399 | resp->statusCode = input_GmStatusCode; |
405 | setCStr_String(&d->resp.meta, "Enter query:"); | 400 | setCStr_String(&resp->meta, "Enter query:"); |
406 | d->state = finished_GmRequestState; | 401 | d->state = finished_GmRequestState; |
407 | iNotifyAudience(d, finished, GmRequestFinished); | 402 | iNotifyAudience(d, finished, GmRequestFinished); |
408 | } | 403 | } |
@@ -411,8 +406,10 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
411 | /*----------------------------------------------------------------------------------------------*/ | 406 | /*----------------------------------------------------------------------------------------------*/ |
412 | 407 | ||
413 | void init_GmRequest(iGmRequest *d, iGmCerts *certs) { | 408 | void init_GmRequest(iGmRequest *d, iGmCerts *certs) { |
414 | init_Mutex(&d->mutex); | 409 | d->mtx = new_Mutex(); |
415 | init_GmResponse(&d->resp); | 410 | d->resp = new_GmResponse(); |
411 | d->respLocked = iFalse; | ||
412 | set_Atomic(&d->allowUpdate, iTrue); | ||
416 | init_String(&d->url); | 413 | init_String(&d->url); |
417 | init_Gopher(&d->gopher); | 414 | init_Gopher(&d->gopher); |
418 | d->certs = certs; | 415 | d->certs = certs; |
@@ -427,22 +424,24 @@ void deinit_GmRequest(iGmRequest *d) { | |||
427 | iDisconnectObject(TlsRequest, d->req, readyRead, d); | 424 | iDisconnectObject(TlsRequest, d->req, readyRead, d); |
428 | iDisconnectObject(TlsRequest, d->req, finished, d); | 425 | iDisconnectObject(TlsRequest, d->req, finished, d); |
429 | } | 426 | } |
430 | lock_Mutex(&d->mutex); | 427 | lock_Mutex(d->mtx); |
431 | if (!isFinished_GmRequest(d)) { | 428 | if (!isFinished_GmRequest(d)) { |
432 | unlock_Mutex(&d->mutex); | 429 | unlock_Mutex(d->mtx); |
433 | cancel_GmRequest(d); | 430 | cancel_GmRequest(d); |
434 | d->state = finished_GmRequestState; | 431 | d->state = finished_GmRequestState; |
435 | } | 432 | } |
436 | else { | 433 | else { |
437 | unlock_Mutex(&d->mutex); | 434 | unlock_Mutex(d->mtx); |
438 | } | 435 | } |
439 | iReleasePtr(&d->req); | 436 | iReleasePtr(&d->req); |
440 | deinit_Gopher(&d->gopher); | 437 | deinit_Gopher(&d->gopher); |
441 | delete_Audience(d->finished); | 438 | delete_Audience(d->finished); |
442 | delete_Audience(d->updated); | 439 | delete_Audience(d->updated); |
443 | deinit_GmResponse(&d->resp); | 440 | // delete_GmResponse(d->respPub); |
441 | // deinit_GmResponse(&d->respInt); | ||
442 | delete_GmResponse(d->resp); | ||
444 | deinit_String(&d->url); | 443 | deinit_String(&d->url); |
445 | deinit_Mutex(&d->mutex); | 444 | delete_Mutex(d->mtx); |
446 | } | 445 | } |
447 | 446 | ||
448 | void setUrl_GmRequest(iGmRequest *d, const iString *url) { | 447 | void setUrl_GmRequest(iGmRequest *d, const iString *url) { |
@@ -455,7 +454,9 @@ void submit_GmRequest(iGmRequest *d) { | |||
455 | if (d->state != initialized_GmRequestState) { | 454 | if (d->state != initialized_GmRequestState) { |
456 | return; | 455 | return; |
457 | } | 456 | } |
458 | clear_GmResponse(&d->resp); | 457 | set_Atomic(&d->allowUpdate, iTrue); |
458 | iGmResponse *resp = d->resp; | ||
459 | clear_GmResponse(resp); | ||
459 | iUrl url; | 460 | iUrl url; |
460 | init_Url(&url, &d->url); | 461 | init_Url(&url, &d->url); |
461 | /* Check for special schemes. */ | 462 | /* Check for special schemes. */ |
@@ -466,14 +467,14 @@ void submit_GmRequest(iGmRequest *d) { | |||
466 | if (equalCase_Rangecc(url.scheme, "about")) { | 467 | if (equalCase_Rangecc(url.scheme, "about")) { |
467 | const iBlock *src = aboutPageSource_(url.path); | 468 | const iBlock *src = aboutPageSource_(url.path); |
468 | if (src) { | 469 | if (src) { |
469 | d->resp.statusCode = success_GmStatusCode; | 470 | resp->statusCode = success_GmStatusCode; |
470 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); | 471 | setCStr_String(&resp->meta, "text/gemini; charset=utf-8"); |
471 | set_Block(&d->resp.body, replaceVariables_(src)); | 472 | set_Block(&resp->body, replaceVariables_(src)); |
472 | d->state = receivingBody_GmRequestState; | 473 | d->state = receivingBody_GmRequestState; |
473 | iNotifyAudience(d, updated, GmRequestUpdated); | 474 | iNotifyAudience(d, updated, GmRequestUpdated); |
474 | } | 475 | } |
475 | else { | 476 | else { |
476 | d->resp.statusCode = invalidLocalResource_GmStatusCode; | 477 | resp->statusCode = invalidLocalResource_GmStatusCode; |
477 | } | 478 | } |
478 | d->state = finished_GmRequestState; | 479 | d->state = finished_GmRequestState; |
479 | iNotifyAudience(d, finished, GmRequestFinished); | 480 | iNotifyAudience(d, finished, GmRequestFinished); |
@@ -485,41 +486,41 @@ void submit_GmRequest(iGmRequest *d) { | |||
485 | if (open_File(f, readOnly_FileMode)) { | 486 | if (open_File(f, readOnly_FileMode)) { |
486 | /* TODO: Check supported file types: images, audio */ | 487 | /* TODO: Check supported file types: images, audio */ |
487 | /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ | 488 | /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ |
488 | d->resp.statusCode = success_GmStatusCode; | 489 | resp->statusCode = success_GmStatusCode; |
489 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { | 490 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { |
490 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); | 491 | setCStr_String(&resp->meta, "text/gemini; charset=utf-8"); |
491 | } | 492 | } |
492 | else if (endsWithCase_String(path, ".txt")) { | 493 | else if (endsWithCase_String(path, ".txt")) { |
493 | setCStr_String(&d->resp.meta, "text/plain"); | 494 | setCStr_String(&resp->meta, "text/plain"); |
494 | } | 495 | } |
495 | else if (endsWithCase_String(path, ".png")) { | 496 | else if (endsWithCase_String(path, ".png")) { |
496 | setCStr_String(&d->resp.meta, "image/png"); | 497 | setCStr_String(&resp->meta, "image/png"); |
497 | } | 498 | } |
498 | else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { | 499 | else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { |
499 | setCStr_String(&d->resp.meta, "image/jpeg"); | 500 | setCStr_String(&resp->meta, "image/jpeg"); |
500 | } | 501 | } |
501 | else if (endsWithCase_String(path, ".gif")) { | 502 | else if (endsWithCase_String(path, ".gif")) { |
502 | setCStr_String(&d->resp.meta, "image/gif"); | 503 | setCStr_String(&resp->meta, "image/gif"); |
503 | } | 504 | } |
504 | else if (endsWithCase_String(path, ".wav")) { | 505 | else if (endsWithCase_String(path, ".wav")) { |
505 | setCStr_String(&d->resp.meta, "audio/wave"); | 506 | setCStr_String(&resp->meta, "audio/wave"); |
506 | } | 507 | } |
507 | else if (endsWithCase_String(path, ".ogg")) { | 508 | else if (endsWithCase_String(path, ".ogg")) { |
508 | setCStr_String(&d->resp.meta, "audio/ogg"); | 509 | setCStr_String(&resp->meta, "audio/ogg"); |
509 | } | 510 | } |
510 | else if (endsWithCase_String(path, ".mp3")) { | 511 | else if (endsWithCase_String(path, ".mp3")) { |
511 | setCStr_String(&d->resp.meta, "audio/mpeg"); | 512 | setCStr_String(&resp->meta, "audio/mpeg"); |
512 | } | 513 | } |
513 | else { | 514 | else { |
514 | setCStr_String(&d->resp.meta, "application/octet-stream"); | 515 | setCStr_String(&resp->meta, "application/octet-stream"); |
515 | } | 516 | } |
516 | set_Block(&d->resp.body, collect_Block(readAll_File(f))); | 517 | set_Block(&resp->body, collect_Block(readAll_File(f))); |
517 | d->state = receivingBody_GmRequestState; | 518 | d->state = receivingBody_GmRequestState; |
518 | iNotifyAudience(d, updated, GmRequestUpdated); | 519 | iNotifyAudience(d, updated, GmRequestUpdated); |
519 | } | 520 | } |
520 | else { | 521 | else { |
521 | d->resp.statusCode = failedToOpenFile_GmStatusCode; | 522 | resp->statusCode = failedToOpenFile_GmStatusCode; |
522 | setCStr_String(&d->resp.meta, cstr_String(path)); | 523 | setCStr_String(&resp->meta, cstr_String(path)); |
523 | } | 524 | } |
524 | iRelease(f); | 525 | iRelease(f); |
525 | d->state = finished_GmRequestState; | 526 | d->state = finished_GmRequestState; |
@@ -527,14 +528,14 @@ void submit_GmRequest(iGmRequest *d) { | |||
527 | return; | 528 | return; |
528 | } | 529 | } |
529 | else if (equalCase_Rangecc(url.scheme, "data")) { | 530 | else if (equalCase_Rangecc(url.scheme, "data")) { |
530 | d->resp.statusCode = success_GmStatusCode; | 531 | resp->statusCode = success_GmStatusCode; |
531 | iString *src = collectNewCStr_String(url.scheme.start + 5); | 532 | iString *src = collectNewCStr_String(url.scheme.start + 5); |
532 | iRangecc header = { constBegin_String(src), constBegin_String(src) }; | 533 | iRangecc header = { constBegin_String(src), constBegin_String(src) }; |
533 | while (header.end < constEnd_String(src) && *header.end != ',') { | 534 | while (header.end < constEnd_String(src) && *header.end != ',') { |
534 | header.end++; | 535 | header.end++; |
535 | } | 536 | } |
536 | iBool isBase64 = iFalse; | 537 | iBool isBase64 = iFalse; |
537 | setRange_String(&d->resp.meta, header); | 538 | setRange_String(&resp->meta, header); |
538 | /* Check what's in the header. */ { | 539 | /* Check what's in the header. */ { |
539 | iRangecc entry = iNullRange; | 540 | iRangecc entry = iNullRange; |
540 | while (nextSplit_Rangecc(header, ";", &entry)) { | 541 | while (nextSplit_Rangecc(header, ";", &entry)) { |
@@ -550,7 +551,7 @@ void submit_GmRequest(iGmRequest *d) { | |||
550 | else { | 551 | else { |
551 | set_String(src, collect_String(urlDecode_String(src))); | 552 | set_String(src, collect_String(urlDecode_String(src))); |
552 | } | 553 | } |
553 | set_Block(&d->resp.body, &src->chars); | 554 | set_Block(&resp->body, &src->chars); |
554 | d->state = receivingBody_GmRequestState; | 555 | d->state = receivingBody_GmRequestState; |
555 | iNotifyAudience(d, updated, GmRequestUpdated); | 556 | iNotifyAudience(d, updated, GmRequestUpdated); |
556 | d->state = finished_GmRequestState; | 557 | d->state = finished_GmRequestState; |
@@ -575,7 +576,7 @@ void submit_GmRequest(iGmRequest *d) { | |||
575 | return; | 576 | return; |
576 | } | 577 | } |
577 | else if (!equalCase_Rangecc(url.scheme, "gemini")) { | 578 | else if (!equalCase_Rangecc(url.scheme, "gemini")) { |
578 | d->resp.statusCode = unsupportedProtocol_GmStatusCode; | 579 | resp->statusCode = unsupportedProtocol_GmStatusCode; |
579 | d->state = finished_GmRequestState; | 580 | d->state = finished_GmRequestState; |
580 | iNotifyAudience(d, finished, GmRequestFinished); | 581 | iNotifyAudience(d, finished, GmRequestFinished); |
581 | return; | 582 | return; |
@@ -604,45 +605,63 @@ void cancel_GmRequest(iGmRequest *d) { | |||
604 | cancel_Gopher(&d->gopher); | 605 | cancel_Gopher(&d->gopher); |
605 | } | 606 | } |
606 | 607 | ||
608 | iGmResponse *lockResponse_GmRequest(iGmRequest *d) { | ||
609 | iAssert(!d->respLocked); | ||
610 | lock_Mutex(d->mtx); | ||
611 | d->respLocked = iTrue; | ||
612 | return d->resp; | ||
613 | } | ||
614 | |||
615 | void unlockResponse_GmRequest(iGmRequest *d) { | ||
616 | iAssert(d->respLocked); | ||
617 | d->respLocked = iFalse; | ||
618 | set_Atomic(&d->allowUpdate, iTrue); | ||
619 | unlock_Mutex(d->mtx); | ||
620 | } | ||
621 | |||
607 | iBool isFinished_GmRequest(const iGmRequest *d) { | 622 | iBool isFinished_GmRequest(const iGmRequest *d) { |
608 | iBool done; | 623 | iBool done; |
609 | iGuardMutex(&d->mutex, | 624 | iGuardMutex(d->mtx, |
610 | done = (d->state == finished_GmRequestState || d->state == failure_GmRequestState)); | 625 | done = (d->state == finished_GmRequestState || d->state == failure_GmRequestState)); |
611 | return done; | 626 | return done; |
612 | } | 627 | } |
613 | 628 | ||
614 | enum iGmStatusCode status_GmRequest(const iGmRequest *d) { | 629 | enum iGmStatusCode status_GmRequest(const iGmRequest *d) { |
615 | return d->resp.statusCode; | 630 | enum iGmStatusCode code; |
631 | iGuardMutex(d->mtx, code = d->resp->statusCode); | ||
632 | return code; | ||
616 | } | 633 | } |
617 | 634 | ||
618 | const iString *meta_GmRequest(const iGmRequest *d) { | 635 | const iString *meta_GmRequest(const iGmRequest *d) { |
619 | if (d->state >= receivingBody_GmRequestState) { | 636 | iAssert(isFinished_GmRequest(d)); |
620 | return &d->resp.meta; | 637 | return &d->resp->meta; |
621 | } | ||
622 | return collectNew_String(); | ||
623 | } | 638 | } |
624 | 639 | ||
625 | const iBlock *body_GmRequest(const iGmRequest *d) { | 640 | const iBlock *body_GmRequest(const iGmRequest *d) { |
626 | iBlock *body; | 641 | iAssert(isFinished_GmRequest(d)); |
627 | iGuardMutex(&d->mutex, body = collect_Block(copy_Block(&d->resp.body))); | 642 | return &d->resp->body; |
628 | return body; | ||
629 | } | 643 | } |
630 | 644 | ||
631 | const iString *url_GmRequest(const iGmRequest *d) { | 645 | size_t bodySize_GmRequest(const iGmRequest *d) { |
632 | return &d->url; | 646 | size_t size; |
647 | iGuardMutex(d->mtx, size = size_Block(&d->resp->body)); | ||
648 | return size; | ||
633 | } | 649 | } |
634 | 650 | ||
635 | const iGmResponse *response_GmRequest(const iGmRequest *d) { | 651 | const iString *url_GmRequest(const iGmRequest *d) { |
636 | iAssert(d->state != initialized_GmRequestState); | 652 | return &d->url; |
637 | return &d->resp; | ||
638 | } | 653 | } |
639 | 654 | ||
640 | int certFlags_GmRequest(const iGmRequest *d) { | 655 | int certFlags_GmRequest(const iGmRequest *d) { |
641 | return d->resp.certFlags; | 656 | int flags; |
657 | iGuardMutex(d->mtx, flags = d->resp->certFlags); | ||
658 | return flags; | ||
642 | } | 659 | } |
643 | 660 | ||
644 | iDate certExpirationDate_GmRequest(const iGmRequest *d) { | 661 | iDate certExpirationDate_GmRequest(const iGmRequest *d) { |
645 | return d->resp.certValidUntil; | 662 | iDate expr; |
663 | iGuardMutex(d->mtx, expr = d->resp->certValidUntil); | ||
664 | return expr; | ||
646 | } | 665 | } |
647 | 666 | ||
648 | iDefineClass(GmRequest) | 667 | iDefineClass(GmRequest) |
diff --git a/src/gmrequest.h b/src/gmrequest.h index 31059a44..bd340cf1 100644 --- a/src/gmrequest.h +++ b/src/gmrequest.h | |||
@@ -68,12 +68,15 @@ void setUrl_GmRequest (iGmRequest *, const iString *ur | |||
68 | void submit_GmRequest (iGmRequest *); | 68 | void submit_GmRequest (iGmRequest *); |
69 | void cancel_GmRequest (iGmRequest *); | 69 | void cancel_GmRequest (iGmRequest *); |
70 | 70 | ||
71 | iGmResponse * lockResponse_GmRequest (iGmRequest *); | ||
72 | void unlockResponse_GmRequest (iGmRequest *); | ||
73 | |||
71 | iBool isFinished_GmRequest (const iGmRequest *); | 74 | iBool isFinished_GmRequest (const iGmRequest *); |
72 | enum iGmStatusCode status_GmRequest (const iGmRequest *); | 75 | enum iGmStatusCode status_GmRequest (const iGmRequest *); |
73 | const iString * meta_GmRequest (const iGmRequest *); | 76 | const iString * meta_GmRequest (const iGmRequest *); |
74 | const iBlock * body_GmRequest (const iGmRequest *); | 77 | const iBlock * body_GmRequest (const iGmRequest *); |
78 | size_t bodySize_GmRequest (const iGmRequest *); | ||
75 | const iString * url_GmRequest (const iGmRequest *); | 79 | const iString * url_GmRequest (const iGmRequest *); |
76 | const iGmResponse * response_GmRequest (const iGmRequest *); | ||
77 | 80 | ||
78 | int certFlags_GmRequest (const iGmRequest *); | 81 | int certFlags_GmRequest (const iGmRequest *); |
79 | iDate certExpirationDate_GmRequest(const iGmRequest *); | 82 | iDate certExpirationDate_GmRequest(const iGmRequest *); |
diff --git a/src/media.c b/src/media.c index cd3dfb82..8bd635a5 100644 --- a/src/media.c +++ b/src/media.c | |||
@@ -154,12 +154,13 @@ void clear_Media(iMedia *d) { | |||
154 | clear_PtrArray(&d->audio); | 154 | clear_PtrArray(&d->audio); |
155 | } | 155 | } |
156 | 156 | ||
157 | void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, | 157 | iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, |
158 | int flags) { | 158 | int flags) { |
159 | const iBool isPartial = (flags & partialData_MediaFlag) != 0; | 159 | const iBool isPartial = (flags & partialData_MediaFlag) != 0; |
160 | const iBool allowHide = (flags & allowHide_MediaFlag) != 0; | 160 | const iBool allowHide = (flags & allowHide_MediaFlag) != 0; |
161 | const iBool isDeleting = (!mime || !data); | 161 | const iBool isDeleting = (!mime || !data); |
162 | iMediaId existing = findLinkImage_Media(d, linkId); | 162 | iMediaId existing = findLinkImage_Media(d, linkId); |
163 | iBool isNew = iFalse; | ||
163 | if (existing) { | 164 | if (existing) { |
164 | iGmImage *img; | 165 | iGmImage *img; |
165 | if (isDeleting) { | 166 | if (isDeleting) { |
@@ -205,6 +206,7 @@ void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBloc | |||
205 | if (!isPartial) { | 206 | if (!isPartial) { |
206 | makeTexture_GmImage(img); | 207 | makeTexture_GmImage(img); |
207 | } | 208 | } |
209 | isNew = iTrue; | ||
208 | } | 210 | } |
209 | else if (startsWith_String(mime, "audio/")) { | 211 | else if (startsWith_String(mime, "audio/")) { |
210 | iGmAudio *audio = new_GmAudio(); | 212 | iGmAudio *audio = new_GmAudio(); |
@@ -219,8 +221,10 @@ void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBloc | |||
219 | /* Start playing right away. */ | 221 | /* Start playing right away. */ |
220 | start_Player(audio->player); | 222 | start_Player(audio->player); |
221 | postCommandf_App("media.player.started player:%p", audio->player); | 223 | postCommandf_App("media.player.started player:%p", audio->player); |
224 | isNew = iTrue; | ||
222 | } | 225 | } |
223 | } | 226 | } |
227 | return isNew; | ||
224 | } | 228 | } |
225 | 229 | ||
226 | iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { | 230 | iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { |
diff --git a/src/media.h b/src/media.h index 12334936..ddaa2d3c 100644 --- a/src/media.h +++ b/src/media.h | |||
@@ -54,7 +54,7 @@ enum iMediaFlags { | |||
54 | }; | 54 | }; |
55 | 55 | ||
56 | void clear_Media (iMedia *); | 56 | void clear_Media (iMedia *); |
57 | void setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); | 57 | iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); |
58 | 58 | ||
59 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); | 59 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); |
60 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); | 60 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index d7d15a67..a843e840 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -766,7 +766,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
766 | 766 | ||
767 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { | 767 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { |
768 | iLabelWidget *prog = findWidget_App("document.progress"); | 768 | iLabelWidget *prog = findWidget_App("document.progress"); |
769 | const size_t dlSize = d->request ? size_Block(body_GmRequest(d->request)) : 0; | 769 | const size_t dlSize = d->request ? bodySize_GmRequest(d->request) : 0; |
770 | setFlags_Widget(as_Widget(prog), hidden_WidgetFlag, dlSize < 250000); | 770 | setFlags_Widget(as_Widget(prog), hidden_WidgetFlag, dlSize < 250000); |
771 | if (isVisible_Widget(prog)) { | 771 | if (isVisible_Widget(prog)) { |
772 | updateText_LabelWidget(prog, | 772 | updateText_LabelWidget(prog, |
@@ -1032,9 +1032,10 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1032 | if (statusCode == none_GmStatusCode) { | 1032 | if (statusCode == none_GmStatusCode) { |
1033 | return; | 1033 | return; |
1034 | } | 1034 | } |
1035 | iGmResponse *resp = lockResponse_GmRequest(d->request); | ||
1035 | if (d->state == fetching_RequestState) { | 1036 | if (d->state == fetching_RequestState) { |
1036 | d->state = receivedPartialResponse_RequestState; | 1037 | d->state = receivedPartialResponse_RequestState; |
1037 | updateTrust_DocumentWidget_(d, response_GmRequest(d->request)); | 1038 | updateTrust_DocumentWidget_(d, resp); |
1038 | init_Anim(&d->sideOpacity, 0); | 1039 | init_Anim(&d->sideOpacity, 0); |
1039 | switch (category_GmStatusCode(statusCode)) { | 1040 | switch (category_GmStatusCode(statusCode)) { |
1040 | case categoryInput_GmStatusCode: { | 1041 | case categoryInput_GmStatusCode: { |
@@ -1045,9 +1046,9 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1045 | as_Widget(d), | 1046 | as_Widget(d), |
1046 | NULL, | 1047 | NULL, |
1047 | format_CStr(uiHeading_ColorEscape "%s", cstr_Rangecc(parts.host)), | 1048 | format_CStr(uiHeading_ColorEscape "%s", cstr_Rangecc(parts.host)), |
1048 | isEmpty_String(meta_GmRequest(d->request)) | 1049 | isEmpty_String(&resp->meta) |
1049 | ? format_CStr("Please enter input for %s:", cstr_Rangecc(parts.path)) | 1050 | ? format_CStr("Please enter input for %s:", cstr_Rangecc(parts.path)) |
1050 | : cstr_String(meta_GmRequest(d->request)), | 1051 | : cstr_String(&resp->meta), |
1051 | uiTextCaution_ColorEscape "Send \u21d2", | 1052 | uiTextCaution_ColorEscape "Send \u21d2", |
1052 | "document.input.submit"); | 1053 | "document.input.submit"); |
1053 | setSensitive_InputWidget(findChild_Widget(dlg, "input"), | 1054 | setSensitive_InputWidget(findChild_Widget(dlg, "input"), |
@@ -1057,15 +1058,15 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1057 | case categorySuccess_GmStatusCode: | 1058 | case categorySuccess_GmStatusCode: |
1058 | init_Anim(&d->scrollY, 0); | 1059 | init_Anim(&d->scrollY, 0); |
1059 | reset_GmDocument(d->doc); /* new content incoming */ | 1060 | reset_GmDocument(d->doc); /* new content incoming */ |
1060 | updateDocument_DocumentWidget_(d, response_GmRequest(d->request), iTrue); | 1061 | updateDocument_DocumentWidget_(d, resp, iTrue); |
1061 | break; | 1062 | break; |
1062 | case categoryRedirect_GmStatusCode: | 1063 | case categoryRedirect_GmStatusCode: |
1063 | if (isEmpty_String(meta_GmRequest(d->request))) { | 1064 | if (isEmpty_String(&resp->meta)) { |
1064 | showErrorPage_DocumentWidget_(d, invalidRedirect_GmStatusCode, NULL); | 1065 | showErrorPage_DocumentWidget_(d, invalidRedirect_GmStatusCode, NULL); |
1065 | } | 1066 | } |
1066 | else { | 1067 | else { |
1067 | /* Only accept redirects that use gemini scheme. */ | 1068 | /* Only accept redirects that use gemini scheme. */ |
1068 | const iString *dstUrl = absoluteUrl_String(d->mod.url, meta_GmRequest(d->request)); | 1069 | const iString *dstUrl = absoluteUrl_String(d->mod.url, &resp->meta); |
1069 | if (d->redirectCount >= 5) { | 1070 | if (d->redirectCount >= 5) { |
1070 | showErrorPage_DocumentWidget_(d, tooManyRedirects_GmStatusCode, dstUrl); | 1071 | showErrorPage_DocumentWidget_(d, tooManyRedirects_GmStatusCode, dstUrl); |
1071 | } | 1072 | } |
@@ -1085,17 +1086,17 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1085 | break; | 1086 | break; |
1086 | default: | 1087 | default: |
1087 | if (isDefined_GmError(statusCode)) { | 1088 | if (isDefined_GmError(statusCode)) { |
1088 | showErrorPage_DocumentWidget_(d, statusCode, meta_GmRequest(d->request)); | 1089 | showErrorPage_DocumentWidget_(d, statusCode, &resp->meta); |
1089 | } | 1090 | } |
1090 | else if (category_GmStatusCode(statusCode) == | 1091 | else if (category_GmStatusCode(statusCode) == |
1091 | categoryTemporaryFailure_GmStatusCode) { | 1092 | categoryTemporaryFailure_GmStatusCode) { |
1092 | showErrorPage_DocumentWidget_( | 1093 | showErrorPage_DocumentWidget_( |
1093 | d, temporaryFailure_GmStatusCode, meta_GmRequest(d->request)); | 1094 | d, temporaryFailure_GmStatusCode, &resp->meta); |
1094 | } | 1095 | } |
1095 | else if (category_GmStatusCode(statusCode) == | 1096 | else if (category_GmStatusCode(statusCode) == |
1096 | categoryPermanentFailure_GmStatusCode) { | 1097 | categoryPermanentFailure_GmStatusCode) { |
1097 | showErrorPage_DocumentWidget_( | 1098 | showErrorPage_DocumentWidget_( |
1098 | d, permanentFailure_GmStatusCode, meta_GmRequest(d->request)); | 1099 | d, permanentFailure_GmStatusCode, &resp->meta); |
1099 | } | 1100 | } |
1100 | break; | 1101 | break; |
1101 | } | 1102 | } |
@@ -1104,12 +1105,13 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1104 | switch (category_GmStatusCode(statusCode)) { | 1105 | switch (category_GmStatusCode(statusCode)) { |
1105 | case categorySuccess_GmStatusCode: | 1106 | case categorySuccess_GmStatusCode: |
1106 | /* More content available. */ | 1107 | /* More content available. */ |
1107 | updateDocument_DocumentWidget_(d, response_GmRequest(d->request), iFalse); | 1108 | updateDocument_DocumentWidget_(d, resp, iFalse); |
1108 | break; | 1109 | break; |
1109 | default: | 1110 | default: |
1110 | break; | 1111 | break; |
1111 | } | 1112 | } |
1112 | } | 1113 | } |
1114 | unlockResponse_GmRequest(d->request); | ||
1113 | } | 1115 | } |
1114 | 1116 | ||
1115 | static const char *sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 1117 | static const char *sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { |
@@ -1190,18 +1192,21 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
1190 | /* Pass new data to media players. */ | 1192 | /* Pass new data to media players. */ |
1191 | const enum iGmStatusCode code = status_GmRequest(req->req); | 1193 | const enum iGmStatusCode code = status_GmRequest(req->req); |
1192 | if (isSuccess_GmStatusCode(code)) { | 1194 | if (isSuccess_GmStatusCode(code)) { |
1193 | if (startsWith_String(meta_GmRequest(req->req), "audio/")) { | 1195 | iGmResponse *resp = lockResponse_GmRequest(req->req); |
1196 | if (startsWith_String(&resp->meta, "audio/")) { | ||
1194 | /* TODO: Use a helper? This is same as below except for the partialData flag. */ | 1197 | /* TODO: Use a helper? This is same as below except for the partialData flag. */ |
1195 | setData_Media(media_GmDocument(d->doc), | 1198 | if (setData_Media(media_GmDocument(d->doc), |
1196 | req->linkId, | 1199 | req->linkId, |
1197 | meta_GmRequest(req->req), | 1200 | &resp->meta, |
1198 | body_GmRequest(req->req), | 1201 | &resp->body, |
1199 | partialData_MediaFlag | allowHide_MediaFlag); | 1202 | partialData_MediaFlag | allowHide_MediaFlag)) { |
1200 | redoLayout_GmDocument(d->doc); | 1203 | redoLayout_GmDocument(d->doc); |
1204 | } | ||
1201 | updateVisible_DocumentWidget_(d); | 1205 | updateVisible_DocumentWidget_(d); |
1202 | invalidate_DocumentWidget_(d); | 1206 | invalidate_DocumentWidget_(d); |
1203 | refresh_Widget(as_Widget(d)); | 1207 | refresh_Widget(as_Widget(d)); |
1204 | } | 1208 | } |
1209 | unlockResponse_GmRequest(req->req); | ||
1205 | } | 1210 | } |
1206 | /* Update the link's progress. */ | 1211 | /* Update the link's progress. */ |
1207 | invalidateLink_DocumentWidget_(d, req->linkId); | 1212 | invalidateLink_DocumentWidget_(d, req->linkId); |
@@ -1482,8 +1487,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1482 | return iTrue; | 1487 | return iTrue; |
1483 | } | 1488 | } |
1484 | else if (equalWidget_Command(cmd, w, "document.request.updated") && | 1489 | else if (equalWidget_Command(cmd, w, "document.request.updated") && |
1485 | pointerLabel_Command(cmd, "request") == d->request) { | 1490 | d->request && pointerLabel_Command(cmd, "request") == d->request) { |
1486 | set_Block(&d->sourceContent, body_GmRequest(d->request)); | 1491 | set_Block(&d->sourceContent, &lockResponse_GmRequest(d->request)->body); |
1492 | unlockResponse_GmRequest(d->request); | ||
1487 | if (document_App() == d) { | 1493 | if (document_App() == d) { |
1488 | updateFetchProgress_DocumentWidget_(d); | 1494 | updateFetchProgress_DocumentWidget_(d); |
1489 | } | 1495 | } |
@@ -1501,7 +1507,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1501 | /* The response may be cached. */ { | 1507 | /* The response may be cached. */ { |
1502 | if (!equal_Rangecc(urlScheme_String(d->mod.url), "about") && | 1508 | if (!equal_Rangecc(urlScheme_String(d->mod.url), "about") && |
1503 | startsWithCase_String(meta_GmRequest(d->request), "text/")) { | 1509 | startsWithCase_String(meta_GmRequest(d->request), "text/")) { |
1504 | setCachedResponse_History(d->mod.history, response_GmRequest(d->request)); | 1510 | setCachedResponse_History(d->mod.history, lockResponse_GmRequest(d->request)); |
1511 | unlockResponse_GmRequest(d->request); | ||
1505 | } | 1512 | } |
1506 | } | 1513 | } |
1507 | iReleasePtr(&d->request); | 1514 | iReleasePtr(&d->request); |
@@ -2496,7 +2503,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
2496 | topRight_Rect(linkRect), | 2503 | topRight_Rect(linkRect), |
2497 | tmInlineContentMetadata_ColorId, | 2504 | tmInlineContentMetadata_ColorId, |
2498 | " \u2014 Fetching\u2026 (%.1f MB)", | 2505 | " \u2014 Fetching\u2026 (%.1f MB)", |
2499 | (float) size_Block(body_GmRequest(mr->req)) / 1.0e6f); | 2506 | (float) bodySize_GmRequest(mr->req) / 1.0e6f); |
2500 | } | 2507 | } |
2501 | } | 2508 | } |
2502 | else if (isHover) { | 2509 | else if (isHover) { |