| 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); |
| 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 } |