comparison src/util.c @ 13200:33bef17125c2

[gaim-migrate @ 15563] This is the soon-to-be-infamous nonblocking network activity patch that I've been working on. Feel free to yell at me if this makes you unhappy. committer: Tailor Script <tailor@pidgin.im>
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 09 Feb 2006 04:17:56 +0000
parents b230ed49c5d1
children 15b3926e2147
comparison
equal deleted inserted replaced
13199:d8f238864c88 13200:33bef17125c2
47 char *url; 47 char *url;
48 gboolean full; 48 gboolean full;
49 char *user_agent; 49 char *user_agent;
50 gboolean http11; 50 gboolean http11;
51 char *request; 51 char *request;
52 gsize request_written;
52 gboolean include_headers; 53 gboolean include_headers;
53 54
54 int inpa; 55 int inpa;
55 56
56 gboolean sentreq; 57 gboolean got_headers;
57 gboolean startsaving;
58 gboolean has_explicit_data_len; 58 gboolean has_explicit_data_len;
59 char *webdata; 59 char *webdata;
60 unsigned long len; 60 unsigned long len;
61 unsigned long data_len; 61 unsigned long data_len;
62 62
3216 } 3216 }
3217 3217
3218 return content_len; 3218 return content_len;
3219 } 3219 }
3220 3220
3221
3221 static void 3222 static void
3222 url_fetched_cb(gpointer url_data, gint sock, GaimInputCondition cond) 3223 url_fetched_cb(gpointer url_data, gint sock, GaimInputCondition cond)
3223 { 3224 {
3224 GaimFetchUrlData *gfud = url_data; 3225 GaimFetchUrlData *gfud = url_data;
3225 int len; 3226 int len;
3226 char buf[4096]; 3227 char buf[4096];
3227 char *data_cursor; 3228 char *data_cursor;
3228 gboolean got_eof = FALSE; 3229 gboolean got_eof = FALSE;
3229 3230
3230 if (sock == -1) 3231 while((len = read(sock, buf, sizeof(buf))) > 0) {
3231 {
3232 gfud->callback(gfud->user_data, NULL, 0);
3233
3234 destroy_fetch_url_data(gfud);
3235
3236 return;
3237 }
3238
3239 if (!gfud->sentreq)
3240 {
3241 char *send;
3242 char buf[1024];
3243
3244 if (gfud->request) {
3245 send = gfud->request;
3246 } else {
3247 if (gfud->user_agent) {
3248 /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
3249 * clients must know how to handle the "chunked" transfer encoding.
3250 * Gaim doesn't know how to handle "chunked", so should always send
3251 * the Host header regardless, to get around some observed problems
3252 */
3253 g_snprintf(buf, sizeof(buf),
3254 "GET %s%s HTTP/%s\r\n"
3255 "Connection: close\r\n"
3256 "User-Agent: %s\r\n"
3257 "Host: %s\r\n\r\n",
3258 (gfud->full ? "" : "/"),
3259 (gfud->full ? gfud->url : gfud->website.page),
3260 (gfud->http11 ? "1.1" : "1.0"),
3261 gfud->user_agent, gfud->website.address);
3262 } else {
3263 g_snprintf(buf, sizeof(buf),
3264 "GET %s%s HTTP/%s\r\n"
3265 "Connection: close\r\n"
3266 "Host: %s\r\n\r\n",
3267 (gfud->full ? "" : "/"),
3268 (gfud->full ? gfud->url : gfud->website.page),
3269 (gfud->http11 ? "1.1" : "1.0"),
3270 gfud->website.address);
3271 }
3272 send = buf;
3273 }
3274
3275 gaim_debug_misc("gaim_url_fetch", "Request: %s\n", send);
3276
3277 write(sock, send, strlen(send));
3278 fcntl(sock, F_SETFL, O_NONBLOCK);
3279 gfud->sentreq = TRUE;
3280 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ,
3281 url_fetched_cb, url_data);
3282 gfud->data_len = 4096;
3283 gfud->webdata = g_malloc(gfud->data_len);
3284
3285 return;
3286 }
3287
3288 while ((len = read(sock, buf, sizeof(buf))) > 0)
3289 {
3290 /* If we've filled up our butfer, make it bigger */ 3232 /* If we've filled up our butfer, make it bigger */
3291 if ((gfud->len + len) >= gfud->data_len) 3233 if((gfud->len + len) >= gfud->data_len) {
3292 { 3234 while((gfud->len + len) >= gfud->data_len)
3293 gfud->data_len += MAX(((gfud->data_len) / 2), sizeof(buf)); 3235 gfud->data_len += sizeof(buf);
3294 3236
3295 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); 3237 gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
3296 } 3238 }
3297 3239
3298 data_cursor = gfud->webdata + gfud->len; 3240 data_cursor = gfud->webdata + gfud->len;
3301 3243
3302 memcpy(data_cursor, buf, len); 3244 memcpy(data_cursor, buf, len);
3303 3245
3304 gfud->webdata[gfud->len] = '\0'; 3246 gfud->webdata[gfud->len] = '\0';
3305 3247
3306 if (!gfud->startsaving) 3248 if(!gfud->got_headers) {
3307 {
3308 char *tmp; 3249 char *tmp;
3309 3250
3310 /** See if we've reached the end of the headers yet */ 3251 /** See if we've reached the end of the headers yet */
3311 if ((tmp = strstr(gfud->webdata, "\r\n\r\n"))) { 3252 if((tmp = strstr(gfud->webdata, "\r\n\r\n"))) {
3312 char * new_data; 3253 char * new_data;
3313 guint header_len = (tmp + 4 - gfud->webdata); 3254 guint header_len = (tmp + 4 - gfud->webdata);
3314 size_t content_len, body_len = 0; 3255 size_t content_len, body_len = 0;
3315 3256
3316 gaim_debug_misc("gaim_url_fetch", "Response headers: '%.*s'\n", 3257 gaim_debug_misc("gaim_url_fetch", "Response headers: '%.*s'\n",
3318 3259
3319 /* See if we can find a redirect. */ 3260 /* See if we can find a redirect. */
3320 if (parse_redirect(gfud->webdata, header_len, sock, gfud)) 3261 if (parse_redirect(gfud->webdata, header_len, sock, gfud))
3321 return; 3262 return;
3322 3263
3323 gfud->startsaving = TRUE; 3264 gfud->got_headers = TRUE;
3324 3265
3325 /* No redirect. See if we can find a content length. */ 3266 /* No redirect. See if we can find a content length. */
3326 content_len = parse_content_len(gfud->webdata, header_len); 3267 content_len = parse_content_len(gfud->webdata, header_len);
3327 3268
3328 if (content_len == 0) 3269 if (content_len == 0)
3381 got_eof = TRUE; 3322 got_eof = TRUE;
3382 break; 3323 break;
3383 } 3324 }
3384 } 3325 }
3385 3326
3386 if (len <= 0) { 3327 if(len <= 0) {
3387 if (errno == EWOULDBLOCK) { 3328 if(errno == EAGAIN) {
3388 errno = 0;
3389 return; 3329 return;
3390 } else if (errno != ETIMEDOUT) { 3330 } else if (errno != ETIMEDOUT) {
3391 got_eof = TRUE; 3331 got_eof = TRUE;
3392 } else { 3332 } else {
3393 gaim_input_remove(gfud->inpa); 3333 gaim_input_remove(gfud->inpa);
3410 close(sock); 3350 close(sock);
3411 gfud->callback(gfud->user_data, gfud->webdata, gfud->len); 3351 gfud->callback(gfud->user_data, gfud->webdata, gfud->len);
3412 3352
3413 destroy_fetch_url_data(gfud); 3353 destroy_fetch_url_data(gfud);
3414 } 3354 }
3355 }
3356
3357 static void
3358 url_fetch_connect_cb(gpointer url_data, gint sock, GaimInputCondition cond) {
3359 GaimFetchUrlData *gfud = url_data;
3360 int len, total_len;
3361
3362 if(sock == -1) {
3363 gfud->callback(gfud->user_data, NULL, 0);
3364 destroy_fetch_url_data(gfud);
3365 return;
3366 }
3367
3368 if (!gfud->request) {
3369 if (gfud->user_agent) {
3370 /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
3371 * clients must know how to handle the "chunked" transfer encoding.
3372 * Gaim doesn't know how to handle "chunked", so should always send
3373 * the Host header regardless, to get around some observed problems
3374 */
3375 gfud->request = g_strdup_printf(
3376 "GET %s%s HTTP/%s\r\n"
3377 "Connection: close\r\n"
3378 "User-Agent: %s\r\n"
3379 "Host: %s\r\n\r\n",
3380 (gfud->full ? "" : "/"),
3381 (gfud->full ? gfud->url : gfud->website.page),
3382 (gfud->http11 ? "1.1" : "1.0"),
3383 gfud->user_agent, gfud->website.address);
3384 } else {
3385 gfud->request = g_strdup_printf(
3386 "GET %s%s HTTP/%s\r\n"
3387 "Connection: close\r\n"
3388 "Host: %s\r\n\r\n",
3389 (gfud->full ? "" : "/"),
3390 (gfud->full ? gfud->url : gfud->website.page),
3391 (gfud->http11 ? "1.1" : "1.0"),
3392 gfud->website.address);
3393 }
3394 }
3395
3396 gaim_debug_misc("gaim_url_fetch", "Request: '%s'\n", gfud->request);
3397
3398 if(!gfud->inpa)
3399 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_WRITE,
3400 url_fetch_connect_cb, gfud);
3401
3402 total_len = strlen(gfud->request);
3403
3404 len = write(sock, gfud->request + gfud->request_written,
3405 total_len - gfud->request_written);
3406
3407 if(len < 0 && errno == EAGAIN)
3408 return;
3409 else if(len < 0) {
3410 gaim_input_remove(gfud->inpa);
3411 close(sock);
3412 gfud->callback(gfud->user_data, NULL, 0);
3413 destroy_fetch_url_data(gfud);
3414 return;
3415 }
3416 gfud->request_written += len;
3417
3418 if(gfud->request_written != total_len)
3419 return;
3420
3421 gaim_input_remove(gfud->inpa);
3422
3423 gfud->inpa = gaim_input_add(sock, GAIM_INPUT_READ, url_fetched_cb,
3424 gfud);
3415 } 3425 }
3416 3426
3417 void 3427 void
3418 gaim_url_fetch_request(const char *url, gboolean full, 3428 gaim_url_fetch_request(const char *url, gboolean full,
3419 const char *user_agent, gboolean http11, 3429 const char *user_agent, gboolean http11,
3442 3452
3443 gaim_url_parse(url, &gfud->website.address, &gfud->website.port, 3453 gaim_url_parse(url, &gfud->website.address, &gfud->website.port,
3444 &gfud->website.page, &gfud->website.user, &gfud->website.passwd); 3454 &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
3445 3455
3446 if (gaim_proxy_connect(NULL, gfud->website.address, 3456 if (gaim_proxy_connect(NULL, gfud->website.address,
3447 gfud->website.port, url_fetched_cb, 3457 gfud->website.port, url_fetch_connect_cb, gfud) != 0) {
3448 gfud) != 0)
3449 {
3450 destroy_fetch_url_data(gfud); 3458 destroy_fetch_url_data(gfud);
3451 3459
3452 cb(user_data, g_strdup(_("g003: Error opening connection.\n")), 0); 3460 cb(user_data, g_strdup(_("g003: Error opening connection.\n")), 0);
3453 } 3461 }
3454 } 3462 }
3729 3737
3730 for(; *str; str = g_utf8_next_char(str)) { 3738 for(; *str; str = g_utf8_next_char(str)) {
3731 gunichar wc = g_utf8_get_char(str); 3739 gunichar wc = g_utf8_get_char(str);
3732 3740
3733 /* super simple check. hopefully not too wrong. */ 3741 /* super simple check. hopefully not too wrong. */
3734 if(wc >= 0x80) { 3742 if(wc >= 0x80) {
3735 g_string_append_printf(out, "&#%u;", (guint32) wc); 3743 g_string_append_printf(out, "&#%u;", (guint32) wc);
3736 } else { 3744 } else {
3737 g_string_append_unichar(out, wc); 3745 g_string_append_unichar(out, wc);
3738 } 3746 }
3739 } 3747 }
3755 out = g_string_new(""); 3763 out = g_string_new("");
3756 3764
3757 while( (b = strstr(buf, "&#")) ) { 3765 while( (b = strstr(buf, "&#")) ) {
3758 gunichar wc; 3766 gunichar wc;
3759 int base = 0; 3767 int base = 0;
3760 3768
3761 /* append everything leading up to the &# */ 3769 /* append everything leading up to the &# */
3762 g_string_append_len(out, buf, b-buf); 3770 g_string_append_len(out, buf, b-buf);
3763 3771
3764 b += 2; /* skip past the &# */ 3772 b += 2; /* skip past the &# */
3765 3773
3766 /* strtoul will handle 0x prefix as hex, but not x */ 3774 /* strtoul will handle 0x prefix as hex, but not x */
3767 if(*b == 'x' || *b == 'X') 3775 if(*b == 'x' || *b == 'X')
3768 base = 16; 3776 base = 16;
3769 3777
3770 /* advances buf to the end of the ncr segment */ 3778 /* advances buf to the end of the ncr segment */
3813 3821
3814 return ret; 3822 return ret;
3815 } 3823 }
3816 3824
3817 /* previously conversation::find_nick() */ 3825 /* previously conversation::find_nick() */
3818 gboolean 3826 gboolean
3819 gaim_utf8_has_word(const char *haystack, const char *needle) 3827 gaim_utf8_has_word(const char *haystack, const char *needle)
3820 { 3828 {
3821 char *hay, *pin, *p; 3829 char *hay, *pin, *p;
3822 int n; 3830 int n;
3823 gboolean ret = FALSE; 3831 gboolean ret = FALSE;