diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 17:32:58 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 17:32:58 +0300 |
commit | d623d42d937d8e7f90deda3f49fe1eb680a7b15e (patch) | |
tree | f0d917942765701ce1a8b374994a640e96b079cc /src/ui/documentwidget.c | |
parent | 2fe3ce64df1924f13a9292e4df013c55e06e3ad1 (diff) |
DocumentWidget: Fetching URL contents
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 31c1a14c..e8a0ded9 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1,20 +1,136 @@ | |||
1 | #include "documentwidget.h" | 1 | #include "documentwidget.h" |
2 | #include "paint.h" | 2 | #include "paint.h" |
3 | 3 | ||
4 | #include <the_Foundation/regexp.h> | ||
5 | #include <the_Foundation/tlsrequest.h> | ||
6 | |||
7 | enum iDocumentState { | ||
8 | blank_DocumentState, | ||
9 | fetching_DocumentState, | ||
10 | layout_DocumentState, | ||
11 | ready_DocumentState, | ||
12 | }; | ||
13 | |||
4 | struct Impl_DocumentWidget { | 14 | struct Impl_DocumentWidget { |
5 | iWidget widget; | 15 | iWidget widget; |
16 | enum iDocumentState state; | ||
17 | iString *url; | ||
18 | iString *source; | ||
19 | int statusCode; | ||
20 | iTlsRequest *request; | ||
6 | }; | 21 | }; |
7 | 22 | ||
23 | iDeclareType(Url) | ||
24 | |||
25 | struct Impl_Url { | ||
26 | iRangecc protocol; | ||
27 | iRangecc host; | ||
28 | iRangecc port; | ||
29 | iRangecc path; | ||
30 | iRangecc query; | ||
31 | }; | ||
32 | |||
33 | void init_Url(iUrl *d, const iString *text) { | ||
34 | iRegExp *pattern = new_RegExp("(.+)://([^/:?]+)(:[0-9]+)?([^?]*)(\\?.*)?", caseInsensitive_RegExpOption); | ||
35 | iRegExpMatch m; | ||
36 | if (matchString_RegExp(pattern, text, &m)) { | ||
37 | capturedRange_RegExpMatch(&m, 1, &d->protocol); | ||
38 | capturedRange_RegExpMatch(&m, 2, &d->host); | ||
39 | capturedRange_RegExpMatch(&m, 3, &d->port); | ||
40 | if (!isEmpty_Range(&d->port)) { | ||
41 | /* Don't include the colon. */ | ||
42 | d->port.start++; | ||
43 | } | ||
44 | capturedRange_RegExpMatch(&m, 4, &d->path); | ||
45 | capturedRange_RegExpMatch(&m, 5, &d->query); | ||
46 | } | ||
47 | else { | ||
48 | iZap(*d); | ||
49 | } | ||
50 | iRelease(pattern); | ||
51 | } | ||
52 | |||
8 | iDefineObjectConstruction(DocumentWidget) | 53 | iDefineObjectConstruction(DocumentWidget) |
9 | 54 | ||
10 | void init_DocumentWidget(iDocumentWidget *d) { | 55 | void init_DocumentWidget(iDocumentWidget *d) { |
11 | iWidget *w = as_Widget(d); | 56 | iWidget *w = as_Widget(d); |
12 | init_Widget(w); | 57 | init_Widget(w); |
13 | setBackgroundColor_Widget(w, gray25_ColorId); | 58 | setBackgroundColor_Widget(w, gray25_ColorId); |
59 | d->state = blank_DocumentState; | ||
60 | d->url = new_String(); | ||
61 | d->statusCode = 0; | ||
62 | d->source = new_String(); | ||
63 | d->request = NULL; | ||
64 | |||
65 | setUrl_DocumentWidget(d, collectNewCStr_String("gemini.circumlunar.space/")); | ||
14 | } | 66 | } |
15 | 67 | ||
16 | void deinit_DocumentWidget(iDocumentWidget *d) { | 68 | void deinit_DocumentWidget(iDocumentWidget *d) { |
69 | delete_String(d->source); | ||
70 | delete_String(d->url); | ||
71 | } | ||
72 | |||
73 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | ||
74 | /* TODO: lock source during update */ | ||
75 | set_String(d->source, source); | ||
76 | printf("%s\n", cstr_String(d->source)); | ||
77 | d->state = layout_DocumentState; | ||
78 | } | ||
79 | |||
80 | static iRangecc getLine_(iRangecc text) { | ||
81 | iRangecc line = { text.start, text.start }; | ||
82 | for (; *line.end != '\n' && line.end != text.end; line.end++) {} | ||
83 | return line; | ||
84 | } | ||
85 | |||
86 | static void requestFinished_DocumentWidget_(iAnyObject *obj) { | ||
87 | iDocumentWidget *d = obj; | ||
88 | iBlock *response = readAll_TlsRequest(d->request); | ||
89 | iRangecc responseRange = { constBegin_Block(response), constEnd_Block(response) }; | ||
90 | iRangecc respLine = getLine_(responseRange); | ||
91 | responseRange.start = respLine.end + 1; | ||
92 | /* First line is the status code. */ { | ||
93 | iString *line = newRange_String(respLine); | ||
94 | trim_String(line); | ||
95 | printf("response: %s\n", cstr_String(line)); | ||
96 | delete_String(line); | ||
97 | } | ||
98 | setSource_DocumentWidget(d, collect_String(newRange_String(responseRange))); | ||
99 | delete_Block(response); | ||
100 | iReleaseLater(d->request); | ||
101 | d->request = NULL; | ||
102 | fflush(stdout); | ||
103 | } | ||
104 | |||
105 | static void fetch_DocumentWidget_(iDocumentWidget *d) { | ||
106 | iAssert(!d->request); | ||
107 | d->state = fetching_DocumentState; | ||
108 | d->statusCode = 0; | ||
109 | iUrl url; | ||
110 | init_Url(&url, d->url); | ||
111 | d->request = new_TlsRequest(); | ||
112 | uint16_t port = toInt_String(collect_String(newRange_String(url.port))); | ||
113 | if (port == 0) { | ||
114 | port = 1965; /* default Gemini port */ | ||
115 | } | ||
116 | setUrl_TlsRequest(d->request, collect_String(newRange_String(url.host)), port); | ||
117 | /* The request string is an UTF-8 encoded absolute URL. */ | ||
118 | iString *content = collectNew_String(); | ||
119 | append_String(content, d->url); | ||
120 | appendCStr_String(content, "\r\n"); | ||
121 | setContent_TlsRequest(d->request, utf8_String(content)); | ||
122 | iConnect(TlsRequest, d->request, finished, d, requestFinished_DocumentWidget_); | ||
123 | submit_TlsRequest(d->request); | ||
124 | } | ||
17 | 125 | ||
126 | void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { | ||
127 | clear_String(d->url); | ||
128 | if (indexOfCStr_String(url, "://") == iInvalidPos && !startsWithCase_String(url, "gemini:")) { | ||
129 | /* Prepend default protocol. */ | ||
130 | setCStr_String(d->url, "gemini://"); | ||
131 | } | ||
132 | append_String(d->url, url); | ||
133 | fetch_DocumentWidget_(d); | ||
18 | } | 134 | } |
19 | 135 | ||
20 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { | 136 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { |
@@ -25,6 +141,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
25 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 141 | static void draw_DocumentWidget_(const iDocumentWidget *d) { |
26 | const iWidget *w = constAs_Widget(d); | 142 | const iWidget *w = constAs_Widget(d); |
27 | draw_Widget(w); | 143 | draw_Widget(w); |
144 | if (d->state != ready_DocumentState) return; | ||
145 | /* TODO: lock source during draw */ | ||
28 | } | 146 | } |
29 | 147 | ||
30 | iBeginDefineSubclass(DocumentWidget, Widget) | 148 | iBeginDefineSubclass(DocumentWidget, Widget) |