summaryrefslogtreecommitdiff
path: root/src/gmrequest.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-11-07 17:38:34 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-11-07 17:38:34 +0200
commit1be45b996e3a974f35519c6b9b6d819b77c4a596 (patch)
tree54183df2b3759baec24f48a24ef928fc8261edb0 /src/gmrequest.c
parent4da916bcb13db2ec340b6b279c14d5556cc0d913 (diff)
GmRequest: Mechanism for Gopher requests
Diffstat (limited to 'src/gmrequest.c')
-rw-r--r--src/gmrequest.c125
1 files changed, 100 insertions, 25 deletions
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 83cbbba7..5baf1620 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32#include <the_Foundation/mutex.h> 32#include <the_Foundation/mutex.h>
33#include <the_Foundation/path.h> 33#include <the_Foundation/path.h>
34#include <the_Foundation/regexp.h> 34#include <the_Foundation/regexp.h>
35#include <the_Foundation/socket.h>
35#include <the_Foundation/tlsrequest.h> 36#include <the_Foundation/tlsrequest.h>
36 37
37#include <SDL_timer.h> 38#include <SDL_timer.h>
@@ -120,6 +121,8 @@ struct Impl_GmRequest {
120 enum iGmRequestState state; 121 enum iGmRequestState state;
121 iString url; 122 iString url;
122 iTlsRequest * req; 123 iTlsRequest * req;
124 iSocket * gopher; /* socket for Gopher connections */
125 char gopherType;
123 iGmResponse resp; 126 iGmResponse resp;
124 iAudience * updated; 127 iAudience * updated;
125 iAudience * finished; 128 iAudience * finished;
@@ -160,13 +163,12 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) {
160 } 163 }
161} 164}
162 165
163static void readIncoming_GmRequest_(iAnyObject *obj) { 166static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) {
164 iGmRequest *d = (iGmRequest *) obj;
165 iBool notifyUpdate = iFalse; 167 iBool notifyUpdate = iFalse;
166 iBool notifyDone = iFalse; 168 iBool notifyDone = iFalse;
167 lock_Mutex(&d->mutex); 169 lock_Mutex(&d->mutex);
168 iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ 170 iAssert(d->state != finished_GmRequestState); /* notifications out of order? */
169 iBlock *data = readAll_TlsRequest(d->req); 171 iBlock *data = readAll_TlsRequest(req);
170 if (d->state == receivingHeader_GmRequestState) { 172 if (d->state == receivingHeader_GmRequestState) {
171 appendCStrN_String(&d->resp.meta, constData_Block(data), size_Block(data)); 173 appendCStrN_String(&d->resp.meta, constData_Block(data), size_Block(data));
172 /* Check if the header line is complete. */ 174 /* Check if the header line is complete. */
@@ -199,14 +201,10 @@ static void readIncoming_GmRequest_(iAnyObject *obj) {
199 d->state = receivingBody_GmRequestState; 201 d->state = receivingBody_GmRequestState;
200 checkServerCertificate_GmRequest_(d); 202 checkServerCertificate_GmRequest_(d);
201 notifyUpdate = iTrue; 203 notifyUpdate = iTrue;
202 /* Start a timeout for the remainder of the response, in case the connection
203 remains open. */
204// restartTimeout_GmRequest_(d);
205 } 204 }
206 } 205 }
207 else if (d->state == receivingBody_GmRequestState) { 206 else if (d->state == receivingBody_GmRequestState) {
208 append_Block(&d->resp.body, data); 207 append_Block(&d->resp.body, data);
209// restartTimeout_GmRequest_(d);
210 notifyUpdate = iTrue; 208 notifyUpdate = iTrue;
211 } 209 }
212 initCurrent_Time(&d->resp.when); 210 initCurrent_Time(&d->resp.when);
@@ -220,22 +218,20 @@ static void readIncoming_GmRequest_(iAnyObject *obj) {
220 } 218 }
221} 219}
222 220
223static void requestFinished_GmRequest_(iAnyObject *obj) { 221static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) {
224 iGmRequest *d = (iGmRequest *) obj; 222 iAssert(req == d->req);
225 lock_Mutex(&d->mutex); 223 lock_Mutex(&d->mutex);
226 /* There shouldn't be anything left to read. */ { 224 /* There shouldn't be anything left to read. */ {
227 iBlock *data = readAll_TlsRequest(d->req); 225 iBlock *data = readAll_TlsRequest(req);
228 iAssert(isEmpty_Block(data)); 226 iAssert(isEmpty_Block(data));
229 delete_Block(data); 227 delete_Block(data);
230 initCurrent_Time(&d->resp.when); 228 initCurrent_Time(&d->resp.when);
231 } 229 }
232// SDL_RemoveTimer(d->timeoutId); 230 d->state = (status_TlsRequest(req) == error_TlsRequestStatus ? failure_GmRequestState
233// d->timeoutId = 0; 231 : finished_GmRequestState);
234 d->state = (status_TlsRequest(d->req) == error_TlsRequestStatus ? failure_GmRequestState
235 : finished_GmRequestState);
236 if (d->state == failure_GmRequestState) { 232 if (d->state == failure_GmRequestState) {
237 d->resp.statusCode = tlsFailure_GmStatusCode; 233 d->resp.statusCode = tlsFailure_GmStatusCode;
238 set_String(&d->resp.meta, errorMessage_TlsRequest(d->req)); 234 set_String(&d->resp.meta, errorMessage_TlsRequest(req));
239 } 235 }
240 checkServerCertificate_GmRequest_(d); 236 checkServerCertificate_GmRequest_(d);
241 unlock_Mutex(&d->mutex); 237 unlock_Mutex(&d->mutex);
@@ -343,17 +339,87 @@ static const iBlock *replaceVariables_(const iBlock *block) {
343 return block; 339 return block;
344} 340}
345 341
342static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) {
343 iBool notifyUpdate = iFalse;
344 lock_Mutex(&d->mutex);
345 iBlock *data = readAll_Socket(socket);
346 if (!isEmpty_Block(data)) {
347 append_Block(&d->resp.body, data);
348 notifyUpdate = iTrue;
349 }
350 delete_Block(data);
351 unlock_Mutex(&d->mutex);
352 if (notifyUpdate) {
353 iNotifyAudience(d, updated, GmRequestUpdated);
354 }
355}
356
357static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) {
358 iUnused(socket);
359 iBool notify = iFalse;
360// gopherRead_GmRequest_(d, socket);
361 lock_Mutex(&d->mutex);
362 if (d->state != failure_GmRequestState) {
363 d->state = finished_GmRequestState;
364 notify = iTrue;
365 /* TODO: Convert the received text into text/gemini. */
366 setCStr_String(&d->resp.meta, "text/plain");
367 d->resp.statusCode = success_GmStatusCode;
368 }
369 unlock_Mutex(&d->mutex);
370 if (notify) {
371 iNotifyAudience(d, finished, GmRequestFinished);
372 }
373}
374
375static void gopherError_GmRequest_(iGmRequest *d, iSocket *socket, int error, const char *msg) {
376 iUnused(socket);
377 lock_Mutex(&d->mutex);
378 d->state = failure_GmRequestState;
379 d->resp.statusCode = tlsFailure_GmStatusCode;
380 format_String(&d->resp.meta, "(%d) %s", error, msg);
381 unlock_Mutex(&d->mutex);
382 iNotifyAudience(d, finished, GmRequestFinished);
383}
384
385static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, uint16_t port) {
386 if (port == 0) {
387 port = 70; /* default port */
388 }
389 d->state = receivingBody_GmRequestState;
390 d->gopher = new_Socket(cstr_String(host), port);
391 iConnect(Socket, d->gopher, readyRead, d, gopherRead_GmRequest_);
392 iConnect(Socket, d->gopher, disconnected, d, gopherDisconnected_GmRequest_);
393 iConnect(Socket, d->gopher, error, d, gopherError_GmRequest_);
394 open_Socket(d->gopher);
395 iUrl parts;
396 init_Url(&parts, &d->url);
397 if (!isEmpty_Range(&parts.path)) {
398 if (*parts.path.start == '/') {
399 parts.path.start++;
400 }
401 d->gopherType = *parts.path.start;
402 while (*parts.path.start != '/' && parts.path.start < parts.path.end) {
403 parts.path.start++;
404 }
405 }
406 writeData_Socket(d->gopher, parts.path.start, size_Range(&parts.path));
407 writeData_Socket(d->gopher, "\r\n", 2);
408}
409
346/*----------------------------------------------------------------------------------------------*/ 410/*----------------------------------------------------------------------------------------------*/
347 411
348void init_GmRequest(iGmRequest *d, iGmCerts *certs) { 412void init_GmRequest(iGmRequest *d, iGmCerts *certs) {
349 init_Mutex(&d->mutex); 413 init_Mutex(&d->mutex);
350 init_GmResponse(&d->resp); 414 init_GmResponse(&d->resp);
351 init_String(&d->url); 415 init_String(&d->url);
352 d->certs = certs; 416 d->certs = certs;
353 d->req = NULL; 417 d->req = NULL;
354 d->state = initialized_GmRequestState; 418 d->gopher = NULL;
355 d->updated = NULL; 419 d->gopherType = 0;
356 d->finished = NULL; 420 d->updated = NULL;
421 d->finished = NULL;
422 d->state = initialized_GmRequestState;
357} 423}
358 424
359void deinit_GmRequest(iGmRequest *d) { 425void deinit_GmRequest(iGmRequest *d) {
@@ -364,14 +430,14 @@ void deinit_GmRequest(iGmRequest *d) {
364 lock_Mutex(&d->mutex); 430 lock_Mutex(&d->mutex);
365 if (!isFinished_GmRequest(d)) { 431 if (!isFinished_GmRequest(d)) {
366 unlock_Mutex(&d->mutex); 432 unlock_Mutex(&d->mutex);
367 cancel_TlsRequest(d->req); 433 cancel_GmRequest(d);
368 d->state = finished_GmRequestState; 434 d->state = finished_GmRequestState;
369 } 435 }
370 else { 436 else {
371 unlock_Mutex(&d->mutex); 437 unlock_Mutex(&d->mutex);
372 } 438 }
373 iRelease(d->req); 439 iReleasePtr(&d->req);
374 d->req = NULL; 440 iReleasePtr(&d->gopher);
375 delete_Audience(d->finished); 441 delete_Audience(d->finished);
376 delete_Audience(d->updated); 442 delete_Audience(d->updated);
377 deinit_GmResponse(&d->resp); 443 deinit_GmResponse(&d->resp);
@@ -504,6 +570,10 @@ void submit_GmRequest(iGmRequest *d) {
504 port = 0; 570 port = 0;
505 } 571 }
506 } 572 }
573 else if (equalCase_Rangecc(url.scheme, "gopher")) {
574 beginGopherConnection_GmRequest_(d, host, port);
575 return;
576 }
507 else if (!equalCase_Rangecc(url.scheme, "gemini")) { 577 else if (!equalCase_Rangecc(url.scheme, "gemini")) {
508 d->resp.statusCode = unsupportedProtocol_GmStatusCode; 578 d->resp.statusCode = unsupportedProtocol_GmStatusCode;
509 d->state = finished_GmRequestState; 579 d->state = finished_GmRequestState;
@@ -528,7 +598,12 @@ void submit_GmRequest(iGmRequest *d) {
528} 598}
529 599
530void cancel_GmRequest(iGmRequest *d) { 600void cancel_GmRequest(iGmRequest *d) {
531 cancel_TlsRequest(d->req); 601 if (d->req) {
602 cancel_TlsRequest(d->req);
603 }
604 if (d->gopher) {
605 close_Socket(d->gopher);
606 }
532} 607}
533 608
534iBool isFinished_GmRequest(const iGmRequest *d) { 609iBool isFinished_GmRequest(const iGmRequest *d) {