5 const float URL_FH_CURL = -1;
6 const float URL_FH_STDOUT = -2;
10 .string url_content_type;
17 .url_ready_func url_ready;
18 .entity url_ready_pass;
24 entity url_fromid[NUM_URL_ID];
25 int autocvar__urllib_nextslot;
28 float url_URI_Get_Callback(int id, float status, string data)
30 if (id < MIN_URL_ID) return 0;
32 if (id >= NUM_URL_ID) return 0;
36 if (e.url_rbuf >= 0 || e.url_wbuf >= 0)
38 LOG_INFOF("WARNING: handle %d (%s) has already received data?!?", id + NUM_URL_ID, e.url_url);
42 // whatever happens, we will remove the URL from the list of IDs
43 url_fromid[id] = NULL;
45 // if we get here, we MUST have both buffers cleared
46 if (e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL) error("url_URI_Get_Callback: not a request waiting for data");
52 n = tokenizebyseparator(data, "\n");
53 e.url_rbuf = buf_create();
56 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create");
57 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
65 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create");
66 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
71 for (i = 0; i < n; ++i)
72 bufstr_set(e.url_rbuf, i, argv(i));
73 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
79 e.url_ready(e, e.url_ready_pass, -fabs(status));
87 void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
91 if (strstrofs(url, "://", 0) >= 0)
97 // collect data to a stringbuffer for a POST request
98 // attempts to close will result in a reading handle
100 // create a writing end that does nothing yet
101 e = new_pure(url_single_fopen_file);
102 e.url_url = strzone(url);
103 e.url_content_type = "text/plain";
105 e.url_fh = URL_FH_CURL;
106 e.url_wbuf = buf_create();
109 LOG_INFO("url_single_fopen: out of memory in buf_create");
110 rdy(e, pass, URL_READY_ERROR);
118 e.url_ready_pass = pass;
119 rdy(e, pass, URL_READY_CANWRITE);
125 // get slot for HTTP request
126 for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
127 if (url_fromid[i] == NULL) break;
130 for (i = 0; i < autocvar__urllib_nextslot; ++i)
131 if (url_fromid[i] == NULL) break;
132 if (i >= autocvar__urllib_nextslot)
134 LOG_INFO("url_single_fopen: too many concurrent requests");
135 rdy(NULL, pass, URL_READY_ERROR);
141 if (!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
143 LOG_INFO("url_single_fopen: failure in crypto_uri_postbuf");
144 rdy(NULL, pass, URL_READY_ERROR);
148 // Make a dummy handle object (no buffers at
149 // all). Wait for data to come from the
150 // server, then call the callback
151 e = new_pure(url_single_fopen_file);
152 e.url_url = strzone(url);
153 e.url_fh = URL_FH_CURL;
157 e.url_ready_pass = pass;
161 // make sure this slot won't be reused quickly even on map change
162 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
172 e = new_pure(url_single_fopen_stdout);
173 e.url_fh = URL_FH_STDOUT;
175 e.url_ready_pass = pass;
176 rdy(e, pass, URL_READY_CANWRITE);
179 LOG_INFO("url_single_fopen: cannot open '-' for reading");
180 rdy(NULL, pass, URL_READY_ERROR);
187 fh = fopen(url, mode);
190 rdy(NULL, pass, URL_READY_ERROR);
195 e = new_pure(url_single_fopen_file);
198 e.url_ready_pass = pass;
199 if (mode == FILE_READ) rdy(e, pass, URL_READY_CANREAD);
200 else rdy(e, pass, URL_READY_CANWRITE);
207 void url_fclose(entity e)
211 if (e.url_fh == URL_FH_CURL)
213 if (e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
214 if (e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
215 error("url_fclose: not closable in current state");
220 // we are closing the write end (HTTP POST request)
222 // get slot for HTTP request
223 for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
224 if (url_fromid[i] == NULL) break;
227 for (i = 0; i < autocvar__urllib_nextslot; ++i)
228 if (url_fromid[i] == NULL) break;
229 if (i >= autocvar__urllib_nextslot)
231 LOG_INFO("url_fclose: too many concurrent requests");
232 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
241 if (!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, e.url_content_type, e.url_verb, e.url_wbuf, 0))
243 LOG_INFO("url_fclose: failure in crypto_uri_postbuf");
244 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
251 // delete write end. File handle is now in unusable
252 // state. Wait for data to come from the server, then
259 // make sure this slot won't be reused quickly even on map change
260 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
264 // we have READ all data, just close
265 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
271 else if (e.url_fh == URL_FH_STDOUT)
273 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
280 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
285 // with \n (blame FRIK_FILE)
287 string url_fgets(entity e)
289 if (e.url_fh == URL_FH_CURL)
291 if (e.url_rbuf == -1) error("url_fgets: not readable in current state");
294 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
298 else if (e.url_fh == URL_FH_STDOUT)
306 return fgets(e.url_fh);
310 // without \n (blame FRIK_FILE)
312 void url_fputs(entity e, string s)
314 if (e.url_fh == URL_FH_CURL)
316 if (e.url_wbuf == -1) error("url_fputs: not writable in current state");
318 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
321 else if (e.url_fh == URL_FH_STDOUT)
333 // multi URL object, tries URLs separated by space in sequence
335 void url_multi_ready(entity fh, entity me, float status)
338 if (status == URL_READY_ERROR || status < 0)
340 if (status == -422) // Unprocessable Entity
342 LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing");
343 me.url_ready(fh, me.url_ready_pass, status);
349 n = tokenize_console(me.url_url);
350 if (n <= me.url_attempt)
352 me.url_ready(fh, me.url_ready_pass, status);
357 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
360 me.url_ready(fh, me.url_ready_pass, status);
364 void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass)
367 n = tokenize_console(url);
370 LOG_INFO("url_multi_fopen: need at least one URL");
371 rdy(NULL, pass, URL_READY_ERROR);
375 entity me = new_pure(url_multi);
376 me.url_url = strzone(url);
380 me.url_ready_pass = pass;
381 url_single_fopen(argv(0), mode, url_multi_ready, me);