5 const float URL_FH_CURL = -1;
6 const float URL_FH_STDOUT = -2;
15 .url_ready_func url_ready;
16 .entity url_ready_pass;
22 entity url_fromid[NUM_URL_ID];
23 int autocvar__urllib_nextslot;
25 float url_URI_Get_Callback(int id, float status, string data)
36 if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
38 LOG_INFOF("WARNING: handle %d (%s) has already received data?!?\n", 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)
47 error("url_URI_Get_Callback: not a request waiting for data");
53 n = tokenizebyseparator(data, "\n");
54 e.url_rbuf = buf_create();
57 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n");
58 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
66 LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n");
67 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
72 for(i = 0; i < n; ++i)
73 bufstr_set(e.url_rbuf, i, argv(i));
74 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
80 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
102 e.classname = "url_single_fopen_file";
103 e.url_url = strzone(url);
104 e.url_fh = URL_FH_CURL;
105 e.url_wbuf = buf_create();
108 LOG_INFO("url_single_fopen: out of memory in buf_create\n");
109 rdy(e, pass, URL_READY_ERROR);
110 strunzone(e.url_url);
117 e.url_ready_pass = pass;
118 rdy(e, pass, URL_READY_CANWRITE);
124 // get slot for HTTP request
125 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
126 if(url_fromid[i] == NULL)
130 for(i = 0; i < autocvar__urllib_nextslot; ++i)
131 if(url_fromid[i] == NULL)
133 if(i >= autocvar__urllib_nextslot)
135 LOG_INFO("url_single_fopen: too many concurrent requests\n");
136 rdy(NULL, pass, URL_READY_ERROR);
142 if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
144 LOG_INFO("url_single_fopen: failure in crypto_uri_postbuf\n");
145 rdy(NULL, pass, URL_READY_ERROR);
149 // Make a dummy handle object (no buffers at
150 // all). Wait for data to come from the
151 // server, then call the callback
153 e.classname = "url_single_fopen_file";
154 e.url_url = strzone(url);
155 e.url_fh = URL_FH_CURL;
159 e.url_ready_pass = pass;
163 // make sure this slot won't be reused quickly even on map change
164 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
175 e.classname = "url_single_fopen_stdout";
176 e.url_fh = URL_FH_STDOUT;
178 e.url_ready_pass = pass;
179 rdy(e, pass, URL_READY_CANWRITE);
182 LOG_INFO("url_single_fopen: cannot open '-' for reading\n");
183 rdy(NULL, pass, URL_READY_ERROR);
190 fh = fopen(url, mode);
193 rdy(NULL, pass, URL_READY_ERROR);
199 e.classname = "url_single_fopen_file";
202 e.url_ready_pass = pass;
203 if(mode == FILE_READ)
204 rdy(e, pass, URL_READY_CANREAD);
206 rdy(e, pass, URL_READY_CANWRITE);
212 void url_fclose(entity e)
216 if(e.url_fh == URL_FH_CURL)
218 if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
219 if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
220 error("url_fclose: not closable in current state");
225 // we are closing the write end (HTTP POST request)
227 // get slot for HTTP request
228 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
229 if(url_fromid[i] == NULL)
233 for(i = 0; i < autocvar__urllib_nextslot; ++i)
234 if(url_fromid[i] == NULL)
236 if(i >= autocvar__urllib_nextslot)
238 LOG_INFO("url_fclose: too many concurrent requests\n");
239 e.url_ready(e,e.url_ready_pass, URL_READY_ERROR);
241 strunzone(e.url_url);
248 if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
250 LOG_INFO("url_fclose: failure in crypto_uri_postbuf\n");
251 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
253 strunzone(e.url_url);
258 // delete write end. File handle is now in unusable
259 // state. Wait for data to come from the server, then
266 // make sure this slot won't be reused quickly even on map change
267 cvar_set("_urllib_nextslot", ftos((i + 1) % NUM_URL_ID));
271 // we have READ all data, just close
272 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
274 strunzone(e.url_url);
278 else if(e.url_fh == URL_FH_STDOUT)
280 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
287 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
292 // with \n (blame FRIK_FILE)
293 string url_fgets(entity e)
295 if(e.url_fh == URL_FH_CURL)
298 error("url_fgets: not readable in current state");
301 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
305 else if(e.url_fh == URL_FH_STDOUT)
313 return fgets(e.url_fh);
317 // without \n (blame FRIK_FILE)
318 void url_fputs(entity e, string s)
320 if(e.url_fh == URL_FH_CURL)
323 error("url_fputs: not writable in current state");
325 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
328 else if(e.url_fh == URL_FH_STDOUT)
340 // multi URL object, tries URLs separated by space in sequence
341 void url_multi_ready(entity fh, entity me, float status)
344 if(status == URL_READY_ERROR || status < 0)
346 if(status == -422) // Unprocessable Entity
348 LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n");
349 me.url_ready(fh, me.url_ready_pass, status);
350 strunzone(me.url_url);
355 n = tokenize_console(me.url_url);
356 if(n <= me.url_attempt)
358 me.url_ready(fh, me.url_ready_pass, status);
359 strunzone(me.url_url);
363 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
366 me.url_ready(fh, me.url_ready_pass, status);
368 void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass)
371 n = tokenize_console(url);
374 LOG_INFO("url_multi_fopen: need at least one URL\n");
375 rdy(NULL, pass, URL_READY_ERROR);
381 me.classname = "url_multi";
382 me.url_url = strzone(url);
386 me.url_ready_pass = pass;
387 url_single_fopen(argv(0), mode, url_multi_ready, me);