4 #define URL_FH_STDOUT -2
13 .url_ready_func url_ready;
14 .entity url_ready_pass;
20 entity url_fromid[NUM_URL_ID];
21 float autocvar__urllib_nextslot;
23 float url_URI_Get_Callback(float id, float status, string data)
34 if(e.url_rbuf >= 0 || e.url_wbuf >= 0)
36 print(sprintf("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url));
40 // whatever happens, we will remove the URL from the list of IDs
41 url_fromid[id] = world;
43 // if we get here, we MUST have both buffers cleared
44 if(e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL)
45 error("url_URI_Get_Callback: not a request waiting for data");
51 n = tokenizebyseparator(data, "\n");
52 e.url_rbuf = buf_create();
55 print("url_URI_Get_Callback: out of memory in buf_create\n");
56 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
64 print("url_URI_Get_Callback: out of memory in buf_create\n");
65 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
70 for(i = 0; i < n; ++i)
71 bufstr_set(e.url_rbuf, i, argv(i));
72 e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD);
78 e.url_ready(e, e.url_ready_pass, -fabs(status));
85 void url_single_fopen(string url, float mode, url_ready_func rdy, entity pass)
89 if(strstrofs(url, "://", 0) >= 0)
95 // collect data to a stringbuffer for a POST request
96 // attempts to close will result in a reading handle
98 // create a writing end that does nothing yet
100 e.classname = "url_single_fopen_file";
101 e.url_url = strzone(url);
102 e.url_fh = URL_FH_CURL;
103 e.url_wbuf = buf_create();
106 print("url_single_fopen: out of memory in buf_create\n");
107 rdy(e, pass, URL_READY_ERROR);
108 strunzone(e.url_url);
115 e.url_ready_pass = pass;
116 rdy(e, pass, URL_READY_CANWRITE);
122 // get slot for HTTP request
123 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
124 if(url_fromid[i] == world)
128 for(i = 0; i < autocvar__urllib_nextslot; ++i)
129 if(url_fromid[i] == world)
131 if(i >= autocvar__urllib_nextslot)
133 print("url_single_fopen: too many concurrent requests\n");
134 rdy(world, pass, URL_READY_ERROR);
140 if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0))
142 print("url_single_fopen: failure in crypto_uri_postbuf\n");
143 rdy(world, pass, URL_READY_ERROR);
147 // Make a dummy handle object (no buffers at
148 // all). Wait for data to come from the
149 // server, then call the callback
151 e.classname = "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(mod(i + 1, NUM_URL_ID)));
173 e.classname = "url_single_fopen_stdout";
174 e.url_fh = URL_FH_STDOUT;
176 e.url_ready_pass = pass;
177 rdy(e, pass, URL_READY_CANWRITE);
180 print("url_single_fopen: cannot open '-' for reading\n");
181 rdy(world, pass, URL_READY_ERROR);
188 fh = fopen(url, mode);
191 rdy(world, pass, URL_READY_ERROR);
197 e.classname = "url_single_fopen_file";
200 e.url_ready_pass = pass;
201 if(mode == FILE_READ)
202 rdy(e, pass, URL_READY_CANREAD);
204 rdy(e, pass, URL_READY_CANWRITE);
210 void url_fclose(entity e)
214 if(e.url_fh == URL_FH_CURL)
216 if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request)
217 if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request)
218 error("url_fclose: not closable in current state");
223 // we are closing the write end (HTTP POST request)
225 // get slot for HTTP request
226 for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i)
227 if(url_fromid[i] == world)
231 for(i = 0; i < autocvar__urllib_nextslot; ++i)
232 if(url_fromid[i] == world)
234 if(i >= autocvar__urllib_nextslot)
236 print("url_fclose: too many concurrent requests\n");
237 e.url_ready(e,e.url_ready_pass, URL_READY_ERROR);
239 strunzone(e.url_url);
246 if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0))
248 print("url_fclose: failure in crypto_uri_postbuf\n");
249 e.url_ready(e, e.url_ready_pass, URL_READY_ERROR);
251 strunzone(e.url_url);
256 // delete write end. File handle is now in unusable
257 // state. Wait for data to come from the server, then
264 // make sure this slot won't be reused quickly even on map change
265 cvar_set("_urllib_nextslot", ftos(mod(i + 1, NUM_URL_ID)));
269 // we have READ all data, just close
270 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED);
272 strunzone(e.url_url);
276 else if(e.url_fh == URL_FH_STDOUT)
278 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
285 e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle
290 // with \n (blame FRIK_FILE)
291 string url_fgets(entity e)
293 if(e.url_fh == URL_FH_CURL)
296 error("url_fgets: not readable in current state");
299 s = bufstr_get(e.url_rbuf, e.url_rbufpos);
303 else if(e.url_fh == URL_FH_STDOUT)
311 return fgets(e.url_fh);
315 // without \n (blame FRIK_FILE)
316 void url_fputs(entity e, string s)
318 if(e.url_fh == URL_FH_CURL)
321 error("url_fputs: not writable in current state");
323 bufstr_set(e.url_wbuf, e.url_wbufpos, s);
326 else if(e.url_fh == URL_FH_STDOUT)
338 // multi URL object, tries URLs separated by space in sequence
339 void url_multi_ready(entity fh, entity me, float status)
342 if(status == URL_READY_ERROR || status < 0)
344 if(status == -422) // Unprocessable Entity
346 print("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n");
347 me.url_ready(fh, me.url_ready_pass, status);
348 strunzone(me.url_url);
353 n = tokenize_console(me.url_url);
354 if(n <= me.url_attempt)
356 me.url_ready(fh, me.url_ready_pass, status);
357 strunzone(me.url_url);
361 url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me);
364 me.url_ready(fh, me.url_ready_pass, status);
366 void url_multi_fopen(string url, float mode, url_ready_func rdy, entity pass)
369 n = tokenize_console(url);
372 print("url_multi_fopen: need at least one URL\n");
373 rdy(world, pass, URL_READY_ERROR);
379 me.classname = "url_multi";
380 me.url_url = strzone(url);
384 me.url_ready_pass = pass;
385 url_single_fopen(argv(0), mode, url_multi_ready, me);