src/server/proxy/httpclient.c

changeset 684
48da20bde908
parent 683
db37761a8494
equal deleted inserted replaced
683:db37761a8494 684:48da20bde908
41 static int client_finished(EventHandler *ev, Event *event); 41 static int client_finished(EventHandler *ev, Event *event);
42 42
43 static int client_send_request(HttpClient *client); 43 static int client_send_request(HttpClient *client);
44 static int client_send_request_body(HttpClient *client); 44 static int client_send_request_body(HttpClient *client);
45 static int client_read_response_header(HttpClient *client); 45 static int client_read_response_header(HttpClient *client);
46 static int client_read_response_body(HttpClient *client);
46 47
47 HttpClient* http_client_new(EventHandler *ev) { 48 HttpClient* http_client_new(EventHandler *ev) {
48 CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE); 49 CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE);
49 if(!mp) { 50 if(!mp) {
50 return NULL; 51 return NULL;
81 82
82 void http_client_free(HttpClient *client) { 83 void http_client_free(HttpClient *client) {
83 cxMempoolFree(client->mp); 84 cxMempoolFree(client->mp);
84 header_array_free(client->request_headers); 85 header_array_free(client->request_headers);
85 http_parser_free(client->parser); 86 http_parser_free(client->parser);
87 if(client->stream) {
88 client->stream->st.free(&client->stream->st);
89 }
86 free(client->buffer.inbuf); 90 free(client->buffer.inbuf);
87 free(client->addr); 91 free(client->addr);
88 free(client->method); 92 free(client->method);
89 free(client->uri); 93 free(client->uri);
90 free(client); 94 free(client);
234 cxBufferPutString(&buf, "\r\n"); 238 cxBufferPutString(&buf, "\r\n");
235 } 239 }
236 hdr = hdr->next; 240 hdr = hdr->next;
237 } 241 }
238 cxBufferPutString(&buf, "\r\n"); 242 cxBufferPutString(&buf, "\r\n");
239 client->req_buffer = buf.space; 243 client->transfer_buffer = buf.space;
240 client->req_buffer_alloc = buf.capacity; 244 client->transfer_buffer_alloc = buf.capacity;
241 client->req_buffer_len = buf.size; 245 client->transfer_buffer_len = buf.size;
242 246
243 return 0; 247 return 0;
244 } 248 }
245 249
246 static int client_connected(EventHandler *ev, Event *event) { 250 static int client_connected(EventHandler *ev, Event *event) {
254 return client_io(ev, event); 258 return client_io(ev, event);
255 } 259 }
256 260
257 static int client_io(EventHandler *ev, Event *event) { 261 static int client_io(EventHandler *ev, Event *event) {
258 HttpClient *client = event->cookie; 262 HttpClient *client = event->cookie;
259 if(client->req_buffer_pos < client->req_buffer_len) { 263 if(client->transfer_buffer_pos < client->transfer_buffer_len) {
260 if(client_send_request(client)) { 264 if(client_send_request(client)) {
261 return client->error == 0; 265 return client->error == 0;
262 } 266 }
263 } 267 }
264 268
270 } 274 }
271 275
272 // writing complete, switch to read events 276 // writing complete, switch to read events
273 event->events = EVENT_POLLIN; 277 event->events = EVENT_POLLIN;
274 278
275 279 if(client_read_response_header(client)) {
276 char *buffer; 280 return client->error == 0;
277 size_t nbytes; 281 }
282 if(client_read_response_body(client)) {
283 return client->error == 0;
284 }
285
286 // request finished
287 if(client->response_finished) {
288 client->response_finished(client, client->response_finished_userdata);
289 }
290
291 return 0;
292 }
293
294 static int client_finished(EventHandler *ev, Event *event) {
295 HttpClient *client = event->cookie;
296
297 close(client->socketfd);
298 client->socketfd = -1;
299
300 // request finished
301 if(client->response_finished) {
302 client->response_finished(client, client->response_finished_userdata);
303 }
304
305 return 0;
306 }
307
308 static int client_send_request(HttpClient *client) {
309 size_t nbytes = client->transfer_buffer_len - client->transfer_buffer_pos;
310 ssize_t w;
311 while((w = write(client->socketfd, client->transfer_buffer + client->transfer_buffer_pos, nbytes)) > 0) {
312 client->transfer_buffer_pos += w;
313 nbytes = client->transfer_buffer_len - client->transfer_buffer_pos;
314 if(nbytes == 0) {
315 break;
316 }
317 }
318
319 if(w <= 0) {
320 if(errno != EAGAIN) {
321 // TODO: log correct host
322 log_ereport(LOG_VERBOSE, "http-client %s - %s: write failed: %s", "localhost", client->uri, strerror(errno));
323 client->error = 1;
324 }
325 return 1;
326 }
327
328 return client->transfer_buffer_pos < client->transfer_buffer_len;
329 }
330
331 static int client_send_request_body(HttpClient *client) {
332 size_t rbody_readsize = client->transfer_buffer_alloc;
333 size_t rbody_buf_offset = 0;
334 if(client->req_content_length == -1) {
335 // chunked transfer encoding:
336 // don't fill req_buffer completely, reserve some space for
337 // a chunk header, that will be inserted at the beginning
338 rbody_readsize -= 16;
339 rbody_buf_offset = 16;
340 }
341 while(!client->request_body_complete) {
342 ssize_t r = client->request_body_read(client, client->transfer_buffer + rbody_buf_offset, rbody_readsize, client->request_body_read_userdata);
343 if(r <= 0) {
344 if(r == HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
345 return 1;
346 } else if(r == 0) {
347 // EOF
348 client->request_body_complete = 1;
349 break;
350 } else {
351 // error
352 client->error = 1;
353 return 1;
354 }
355 } else if(client->req_content_length == -1 && r + 32 < rbody_readsize) {
356 // is it time to terminate the request body?
357 // try read some additional bytes, if it returns 0, we know
358 // the request body is complete and we can add the termination chunk
359 char *r2buf = client->transfer_buffer + rbody_buf_offset + r;
360 ssize_t r2 = client->request_body_read(client, r2buf, 32, client->request_body_read_userdata);
361 if(r > 0) {
362 r += r2;
363 } else if(r == 0) {
364 memcpy(r2buf, "0\r\n\r\n", 5);
365 r += 5;
366 client->request_body_complete = 1;
367 client->request_body_terminated = 1;
368 } else if(r == HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
369 return 1;
370 } else {
371 client->error = 1;
372 return 1;
373 }
374 }
375
376 size_t startpos = 0;
377 if(client->req_content_length == -1) {
378 char chunkheader[16];
379 int chunkheaderlen = snprintf(chunkheader, 16, "%zx\r\n", (size_t)r);
380 startpos = 16 - chunkheaderlen;
381 memcpy(client->transfer_buffer + startpos, chunkheader, chunkheaderlen);
382 }
383
384 client->req_contentlength_pos += r;
385 client->transfer_buffer_pos = startpos;
386 client->transfer_buffer_len = rbody_buf_offset + r;
387 if(client_send_request(client)) {
388 return 1;
389 }
390 }
391
392 // chunked transfer encoding: terminate
393 if(client->req_content_length == -1 && !client->request_body_terminated) {
394 memcpy(client->transfer_buffer, "0\r\n\r\n", 5);
395 client->transfer_buffer_pos = 0;
396 client->transfer_buffer_len = 5;
397 client->request_body_terminated = 1;
398 if(client_send_request(client)) {
399 return 1;
400 }
401
402 } else if(client->req_content_length != client->req_contentlength_pos) {
403 // incomplete request body
404 client->error = 1;
405 return 1;
406 }
407
408 return 0;
409 }
410
411 static int client_read_response_header(HttpClient *client) {
278 if(client->response_header_complete) { 412 if(client->response_header_complete) {
279 buffer = client->buffer.inbuf; 413 return 0;
280 nbytes = client->buffer.maxsize; 414 }
281 } else { 415
282 buffer = client->buffer.inbuf + client->buffer.pos; 416 char *buffer = client->buffer.inbuf + client->buffer.pos;
283 nbytes = client->buffer.maxsize - client->buffer.cursize; 417 size_t nbytes = client->buffer.maxsize - client->buffer.cursize;
284 }
285
286 418
287 ssize_t r; 419 ssize_t r;
288 while((r = read(client->socketfd, buffer, nbytes)) > 0) { 420 while((r = read(client->socketfd, buffer, nbytes)) > 0) {
289 client->buffer.cursize += r; 421 client->buffer.cursize += r;
290 if(!client->response_header_complete) { 422 if(!client->response_header_complete) {
291 switch(http_parser_process(client->parser)) { 423 switch(http_parser_process(client->parser)) {
292 case 0: { // finish 424 case 0: { // finish
293 if(!http_parser_validate(client->parser)) { 425 if(!http_parser_validate(client->parser)) {
294 client->error = 1; 426 client->error = 1;
295 return 0; 427 return 1;
296 } 428 }
297 client->statuscode = client->parser->status; 429 client->statuscode = client->parser->status;
298 430
299 client->response_header_complete = 1; 431 client->response_header_complete = 1;
300 if(client->response_start) { 432 if(client->response_start) {
311 case 1: { // need more data 443 case 1: { // need more data
312 continue; 444 continue;
313 } 445 }
314 case 2: { // error 446 case 2: { // error
315 client->error = 1; 447 client->error = 1;
316 return 0; 448 return 1;
317 } 449 }
318 } 450 }
319 } 451 }
320 452
321 // header complete 453 // header complete
322 454 break;
323 char *out = client->buffer.inbuf + client->buffer.pos; 455 }
324 size_t len = client->buffer.cursize - client->buffer.pos; 456
325 457 if(r <= 0) {
326 if(client->response_body_write) { 458 if(r == 0) {
327 int ret = client->response_body_write(client, out, len, client->response_body_write_userdata); 459 // unexpected EOF
328 // TODO: check ret 460 client->error = 1;
329 } 461 } else if(errno != EAGAIN) {
330
331 client->buffer.pos = 0;
332 client->buffer.cursize = 0;
333 }
334
335 if(r < 0) {
336 if(errno == EAGAIN) {
337 return 1;
338 } else {
339 log_ereport(LOG_FAILURE, "http-client: IO error: %s", strerror(errno)); 462 log_ereport(LOG_FAILURE, "http-client: IO error: %s", strerror(errno));
340 }
341 }
342
343 // request finished
344 if(client->response_finished) {
345 client->response_finished(client, client->response_finished_userdata);
346 }
347
348 return 0;
349 }
350
351 static int client_finished(EventHandler *ev, Event *event) {
352 HttpClient *client = event->cookie;
353
354 close(client->socketfd);
355 client->socketfd = -1;
356
357 // request finished
358 if(client->response_finished) {
359 client->response_finished(client, client->response_finished_userdata);
360 }
361
362 return 0;
363 }
364
365 static int client_send_request(HttpClient *client) {
366 size_t nbytes = client->req_buffer_len - client->req_buffer_pos;
367 ssize_t w;
368 while((w = write(client->socketfd, client->req_buffer + client->req_buffer_pos, nbytes)) > 0) {
369 client->req_buffer_pos += w;
370 nbytes = client->req_buffer_len - client->req_buffer_pos;
371 if(nbytes == 0) {
372 break;
373 }
374 }
375
376 if(w <= 0) {
377 if(errno != EAGAIN) {
378 // TODO: log correct host
379 log_ereport(LOG_VERBOSE, "http-client %s - %s: write failed: %s", "localhost", client->uri, strerror(errno));
380 client->error = 1; 463 client->error = 1;
381 } 464 }
382 return 1; 465 return 1;
383 } 466 }
384 467
385 return client->req_buffer_pos < client->req_buffer_len; 468 // initialize httpstream
386 } 469 HeaderArray *headers = client->parser->headers;
387 470 long long contentlength = 0;
388 static int client_send_request_body(HttpClient *client) { 471 int chunkedtransferenc = 0;
389 size_t rbody_readsize = client->req_buffer_alloc; 472 while(headers) {
390 size_t rbody_buf_offset = 0; 473 for(int i=0;i<headers->len;i++) {
391 if(client->req_content_length == -1) { 474 if(!cx_strcasecmp(headers->headers[i].name, "content-length")) {
392 // chunked transfer encoding: 475 if(!cx_strtoll(headers->headers[i].value, &contentlength, 10)) {
393 // don't fill req_buffer completely, reserve some space for 476 headers = NULL;
394 // a chunk header, that will be inserted at the beginning
395 rbody_readsize -= 16;
396 rbody_buf_offset = 16;
397 }
398 while(!client->request_body_complete) {
399 ssize_t r = client->request_body_read(client, client->req_buffer + rbody_buf_offset, rbody_readsize, client->request_body_read_userdata);
400 if(r <= 0) {
401 if(r == HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
402 return 1;
403 } else if(r == 0) {
404 // EOF
405 client->request_body_complete = 1;
406 break;
407 } else {
408 // error
409 client->error = 1;
410 return 1;
411 }
412 } else if(client->req_content_length == -1 && r + 32 < rbody_readsize) {
413 // is it time to terminate the request body?
414 // try read some additional bytes, if it returns 0, we know
415 // the request body is complete and we can add the termination chunk
416 char *r2buf = client->req_buffer + rbody_buf_offset + r;
417 ssize_t r2 = client->request_body_read(client, r2buf, 32, client->request_body_read_userdata);
418 if(r > 0) {
419 r += r2;
420 } else if(r == 0) {
421 memcpy(r2buf, "0\r\n\r\n", 5);
422 r += 5;
423 client->request_body_complete = 1;
424 client->request_body_terminated = 1;
425 } else if(r == HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
426 return 1;
427 } else {
428 client->error = 1;
429 return 1;
430 }
431 }
432
433 size_t startpos = 0;
434 if(client->req_content_length == -1) {
435 char chunkheader[16];
436 int chunkheaderlen = snprintf(chunkheader, 16, "%zx\r\n", (size_t)r);
437 startpos = 16 - chunkheaderlen;
438 memcpy(client->req_buffer + startpos, chunkheader, chunkheaderlen);
439 }
440
441 client->req_contentlength_pos += r;
442 client->req_buffer_pos = startpos;
443 client->req_buffer_len = rbody_buf_offset + r;
444 if(client_send_request(client)) {
445 return 1;
446 }
447 }
448
449 // chunked transfer encoding: terminate
450 if(client->req_content_length == -1 && !client->request_body_terminated) {
451 memcpy(client->req_buffer, "0\r\n\r\n", 5);
452 client->req_buffer_pos = 0;
453 client->req_buffer_len = 5;
454 client->request_body_terminated = 1;
455 if(client_send_request(client)) {
456 return 1;
457 }
458
459 } else if(client->req_content_length != client->req_contentlength_pos) {
460 // incomplete request body
461 client->error = 1;
462 return 1;
463 }
464
465 return 0;
466 }
467
468 /*
469 static int client_read_response_header(HttpClient *client) {
470 if(client->response_header_complete) {
471 return 0;
472 }
473
474 char *buffer = client->buffer.inbuf + client->buffer.pos;
475 size_t nbytes = client->buffer.maxsize - client->buffer.cursize;
476
477 ssize_t r;
478 while((r = read(client->socketfd, buffer, nbytes)) > 0) {
479 client->buffer.cursize += r;
480 if(!client->response_header_complete) {
481 switch(http_parser_process(client->parser)) {
482 case 0: { // finish
483 if(!http_parser_validate(client->parser)) {
484 client->error = 1;
485 return 0;
486 }
487 client->statuscode = client->parser->status;
488
489 client->response_header_complete = 1;
490 if(client->response_start) {
491 cxmutstr msg = client->parser->msg;
492 char t = msg.ptr[msg.length];
493 msg.ptr[msg.length] = 0;
494 int ret = client->response_start(client, client->statuscode, msg.ptr, client->response_start_userdata);
495 msg.ptr[msg.length] = t;
496
497 // TODO: check ret
498 }
499 break; 477 break;
500 } 478 }
501 case 1: { // need more data 479 } else if(!cx_strcasecmp(headers->headers[i].name, "transfer-encoding")) {
502 continue; 480 if(!cx_strcmp(headers->headers[i].value, "chunked")) {
481 chunkedtransferenc = 1;
482 headers = NULL;
483 break;
503 } 484 }
504 case 2: { // error 485 }
505 client->error = 1; 486 }
506 return 0; 487 }
507 } 488
508 } 489 if(contentlength > 0 || chunkedtransferenc) {
509 } 490 IOStream *fd = Sysstream_new(NULL, client->socketfd);
510 491 if(!fd) {
511 // header complete 492 client->error = 1;
512 493 return 1;
513 } 494 }
514 } 495 HttpStream *http = (HttpStream*)httpstream_new(NULL, fd);
515 */ 496 if(!http) {
497 fd->free(fd);
498 }
499 if(contentlength > 0) {
500 http->max_read = contentlength;
501 httpstream_enable_buffered_read(&http->st, (char*)client->buffer.inbuf, client->buffer.maxsize, &client->buffer.cursize, &client->buffer.pos);
502 } else if(chunkedtransferenc) {
503 httpstream_enable_chunked_read(&http->st, (char*)client->buffer.inbuf, client->buffer.maxsize, &client->buffer.cursize, &client->buffer.pos);
504 }
505 client->stream = http;
506 }
507
508 return 0;
509 }
510
511 static int client_read_response_body(HttpClient *client) {
512 if(!client->stream) {
513 return 0; // no input stream -> no response body
514 }
515
516 char *buf = client->transfer_buffer;
517 size_t nbytes = client->transfer_buffer_alloc;
518
519 ssize_t r;
520 while((r = net_read(&client->stream->st, buf, nbytes)) > 0) {
521 if(client->response_body_write) {
522 int ret = client->response_body_write(client, buf, r, client->response_body_write_userdata);
523 // TODO: check ret
524 }
525 }
526
527 if(r < 0) {
528 if(r != HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
529 client->error;
530 }
531 return 1;
532 }
533
534 return 0;
535 }
516 536
517 /* --------------------------------- Tests --------------------------------- */ 537 /* --------------------------------- Tests --------------------------------- */
518 538
519 static CX_TEST(test_http_client_send_request) { 539 static CX_TEST(test_http_client_send_request) {
520 CX_TEST_DO { 540 CX_TEST_DO {
533 size_t len = 32*1024*1024; 553 size_t len = 32*1024*1024;
534 char *str = malloc(len); 554 char *str = malloc(len);
535 // init the buffer with random data 555 // init the buffer with random data
536 for(size_t i=0;i<len;i+=sizeof(int)) { 556 for(size_t i=0;i<len;i+=sizeof(int)) {
537 int *p = (int*)(str+i); 557 int *p = (int*)(str+i);
538 *p = rand();; 558 *p = rand();
539 } 559 }
540 560
541 client->req_buffer = str; 561 client->transfer_buffer = str;
542 client->req_buffer_len = len; 562 client->transfer_buffer_len = len;
543 563
544 // test client_send_request 564 // test client_send_request
545 565
546 int ret = client_send_request(client); 566 int ret = client_send_request(client);
547 // It is very likely that the first client_send_request call doesn't 567 // It is very likely that the first client_send_request call doesn't
548 // fully write the request buffer to the socket 568 // fully write the request buffer to the socket
549 // In that case it returns 1 but without the error flag 569 // In that case it returns 1 but without the error flag
550 CX_TEST_ASSERT(ret == 1 && !client->error); 570 CX_TEST_ASSERT(ret == 1 && !client->error);
551 CX_TEST_ASSERT(client->req_buffer_pos > 0); 571 CX_TEST_ASSERT(client->transfer_buffer_pos > 0);
552 CX_TEST_ASSERT(client->req_buffer_pos < len); 572 CX_TEST_ASSERT(client->transfer_buffer_pos < len);
553 573
554 // read the request buffer from sock and continue with client_send_request 574 // read the request buffer from sock and continue with client_send_request
555 CxBuffer buf; 575 CxBuffer buf;
556 cxBufferInit(&buf, cxDefaultAllocator, NULL, len, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 576 cxBufferInit(&buf, cxDefaultAllocator, NULL, len, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
557 char tmpbuf[1024]; 577 char tmpbuf[1024];
558 int writes = 1; 578 int writes = 1;
559 while(client->req_buffer_pos < client->req_buffer_len && writes < 2000000) { 579 while(client->transfer_buffer_pos < client->transfer_buffer_len && writes < 2000000) {
560 ssize_t r = read(sock, tmpbuf, 1024); 580 ssize_t r = read(sock, tmpbuf, 1024);
561 CX_TEST_ASSERT(r >= 0); 581 CX_TEST_ASSERT(r >= 0);
562 cxBufferWrite(tmpbuf, 1, r, &buf); 582 cxBufferWrite(tmpbuf, 1, r, &buf);
563 ret = client_send_request(client); 583 ret = client_send_request(client);
564 CX_TEST_ASSERT(ret == 0 || (ret == 1 && !client->error)); 584 CX_TEST_ASSERT(ret == 0 || (ret == 1 && !client->error));
565 585
566 writes++; 586 writes++;
567 } 587 }
568 CX_TEST_ASSERT(client->req_buffer_pos == client->req_buffer_len); 588 CX_TEST_ASSERT(client->transfer_buffer_pos == client->transfer_buffer_len);
569 589
570 // finish reading the request buffer from sock 590 // finish reading the request buffer from sock
571 ssize_t r; 591 ssize_t r;
572 while((r = read(sock, tmpbuf, 1024)) > 0 && writes < 2000000) { 592 while((r = read(sock, tmpbuf, 1024)) > 0 && writes < 2000000) {
573 cxBufferWrite(tmpbuf, 1, r, &buf); 593 cxBufferWrite(tmpbuf, 1, r, &buf);
622 http_client_add_request_header(client, cx_mutstr("Host"), cx_mutstr("localhost")); 642 http_client_add_request_header(client, cx_mutstr("Host"), cx_mutstr("localhost"));
623 http_client_add_request_header(client, cx_mutstr("Test1"), cx_mutstr("value1")); 643 http_client_add_request_header(client, cx_mutstr("Test1"), cx_mutstr("value1"));
624 http_client_add_request_header(client, cx_mutstr("Test2"), cx_mutstr("value2")); 644 http_client_add_request_header(client, cx_mutstr("Test2"), cx_mutstr("value2"));
625 create_req_buffer(client); 645 create_req_buffer(client);
626 646
627 size_t req_header_len = client->req_buffer_len; 647 size_t req_header_len = client->transfer_buffer_len;
628 648
629 // response buffer 649 // response buffer
630 CxBuffer buf; 650 CxBuffer buf;
631 cxBufferInit(&buf, cxDefaultAllocator, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 651 cxBufferInit(&buf, cxDefaultAllocator, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
632 652
683 response_str_pos += w; 703 response_str_pos += w;
684 704
685 ret = client_io(&dummy, &event); 705 ret = client_io(&dummy, &event);
686 706
687 CX_TEST_ASSERT(!client->error); 707 CX_TEST_ASSERT(!client->error);
688 CX_TEST_ASSERT(ret == 1);
689 } 708 }
690 CX_TEST_ASSERT(response_str_pos == response_str_len); 709 CX_TEST_ASSERT(response_str_pos == response_str_len);
691 CX_TEST_ASSERT(testr.status == 200); 710 CX_TEST_ASSERT(testr.status == 200);
692 CX_TEST_ASSERT(testr.msg); 711 CX_TEST_ASSERT(testr.msg);
693 CX_TEST_ASSERT(!strcmp(testr.msg, "OK")); 712 CX_TEST_ASSERT(!strcmp(testr.msg, "OK"));
767 req.max_reads = 8; 786 req.max_reads = 8;
768 req.cur_reads = 0; 787 req.cur_reads = 0;
769 client->request_body_read = test_request_body_read; 788 client->request_body_read = test_request_body_read;
770 client->request_body_read_userdata = &req; 789 client->request_body_read_userdata = &req;
771 790
772 memset(client->req_buffer, '_', client->req_buffer_alloc); 791 memset(client->transfer_buffer, '_', client->transfer_buffer_alloc);
773 client->req_buffer_pos = 0; 792 client->transfer_buffer_pos = 0;
774 client->req_buffer_len = 0; 793 client->transfer_buffer_len = 0;
775 794
776 // send the first 128 bytes 795 // send the first 128 bytes
777 while(req.cur_reads <= req.max_reads) { 796 while(req.cur_reads <= req.max_reads) {
778 int ret = client_send_request_body(client); 797 int ret = client_send_request_body(client);
779 CX_TEST_ASSERT(ret == 1); 798 CX_TEST_ASSERT(ret == 1);
838 http_client_free(client); 857 http_client_free(client);
839 cxBufferDestroy(&buf); 858 cxBufferDestroy(&buf);
840 } 859 }
841 } 860 }
842 861
862 static CX_TEST(test_http_client_read_response_head) {
863 CX_TEST_DO {
864 EventHandler dummy;
865 HttpClient *client = http_client_new(&dummy);
866 create_req_buffer(client);
867 client->req_content_length = -1;
868
869 int fds[2];
870 util_socketpair(fds);
871 util_socket_setnonblock(fds[0], 1);
872 util_socket_setnonblock(fds[1], 1);
873 client->socketfd = fds[0];
874 int sock = fds[1];
875
876 // test
877 char *response_str =
878 "HTTP/1.1 204 OK\r\n"
879 "Host: localhost\r\n"
880 "Content-length: 0\r\n"
881 "\r\n";
882
883 size_t response_len = strlen(response_str);
884 size_t response_pos = 0;
885 while(response_pos < response_len) {
886 size_t nbytes = response_len - response_pos;
887 ssize_t w = write(sock, response_str + response_pos, nbytes);
888 if(w > 0) {
889 response_pos += w;
890 }
891
892 if(!client->response_header_complete) {
893 int ret = client_read_response_header(client);
894 CX_TEST_ASSERT(client->error == 0);
895 if(ret == 1) {
896 continue;
897 }
898
899 CX_TEST_ASSERT(client->stream == NULL);
900 }
901
902 break;
903 }
904
905 // cleanup
906 close(fds[0]);
907 close(fds[1]);
908 http_client_free(client);
909 }
910 }
911
843 void http_client_add_tests(CxTestSuite *suite) { 912 void http_client_add_tests(CxTestSuite *suite) {
844 cx_test_register(suite, test_http_client_send_request); 913 cx_test_register(suite, test_http_client_send_request);
845 cx_test_register(suite, test_http_client_send_request_body_chunked); 914 cx_test_register(suite, test_http_client_send_request_body_chunked);
915 cx_test_register(suite, test_http_client_read_response_head);
846 cx_test_register(suite, test_http_client_io_simple); 916 cx_test_register(suite, test_http_client_io_simple);
847 } 917 }

mercurial