1
/* $Id: http_client.c 3841 2011-10-24 09:28:13Z ming $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjlib-util/http_client.h>
21
#include <pj/activesock.h>
22
#include <pj/assert.h>
25
#include <pj/except.h>
27
#include <pj/string.h>
30
#include <pjlib-util/base64.h>
31
#include <pjlib-util/errno.h>
32
#include <pjlib-util/md5.h>
33
#include <pjlib-util/scanner.h>
34
#include <pjlib-util/string.h>
36
#define THIS_FILE "http_client.c"
39
/* Enable some tracing */
40
#define TRACE_(arg) PJ_LOG(3,arg)
45
#define NUM_PROTOCOL 2
46
#define HTTP_1_0 "1.0"
47
#define HTTP_1_1 "1.1"
48
#define CONTENT_LENGTH "Content-Length"
49
/* Buffer size for sending/receiving messages. */
51
/* Initial data buffer size to store the data in case content-
52
* length is not specified in the server's response.
54
#define INITIAL_DATA_BUF_SIZE 2048
55
#define INITIAL_POOL_SIZE 1024
56
#define POOL_INCREMENT_SIZE 512
64
static const char *http_protocol_names[NUM_PROTOCOL] =
70
static const unsigned int http_default_port[NUM_PROTOCOL] =
83
static const char *http_method_names[3] =
105
AUTH_NONE, /* Not authenticating */
106
AUTH_RETRYING, /* New request with auth has been submitted */
107
AUTH_DONE /* Done retrying the request with auth. */
112
pj_str_t url; /* Request URL */
113
pj_http_url hurl; /* Parsed request URL */
114
pj_sockaddr addr; /* The host's socket address */
115
pj_http_req_param param; /* HTTP request parameters */
116
pj_pool_t *pool; /* Pool to allocate memory from */
117
pj_timer_heap_t *timer; /* Timer for timeout management */
118
pj_ioqueue_t *ioqueue; /* Ioqueue to use */
119
pj_http_req_callback cb; /* Callbacks */
120
pj_activesock_t *asock; /* Active socket */
121
pj_status_t error; /* Error status */
122
pj_str_t buffer; /* Buffer to send/receive msgs */
123
enum http_state state; /* State of the HTTP request */
124
enum auth_state auth_state; /* Authentication state */
125
pj_timer_entry timer_entry;/* Timer entry */
126
pj_bool_t resolved; /* Whether URL's host is resolved */
127
pj_http_resp response; /* HTTP response */
128
pj_ioqueue_op_key_t op_key;
131
/* Total data sent so far if the data is sent in segments (i.e.
132
* if on_send_data() is not NULL and if param.reqdata.total_size > 0)
134
pj_size_t tot_chunk_size;
135
/* Size of data to be sent (in a single activesock operation).*/
137
/* Data size sent so far. */
138
pj_size_t current_send_size;
139
/* Total data received so far. */
140
pj_size_t current_read_size;
144
/* Start sending the request */
145
static pj_status_t http_req_start_sending(pj_http_req *hreq);
146
/* Start reading the response */
147
static pj_status_t http_req_start_reading(pj_http_req *hreq);
148
/* End the request */
149
static pj_status_t http_req_end_request(pj_http_req *hreq);
150
/* Parse the header data and populate the header fields with the result. */
151
static pj_status_t http_headers_parse(char *hdata, pj_size_t size,
152
pj_http_headers *headers);
153
/* Parse the response */
154
static pj_status_t http_response_parse(pj_pool_t *pool,
155
pj_http_resp *response,
156
void *data, pj_size_t size,
157
pj_size_t *remainder);
158
/* Restart the request with authentication */
159
static void restart_req_with_auth(pj_http_req *hreq);
160
/* Parse authentication challenge */
161
static pj_status_t parse_auth_chal(pj_pool_t *pool, pj_str_t *input,
162
pj_http_auth_chal *chal);
164
static pj_uint16_t get_http_default_port(const pj_str_t *protocol)
168
for (i = 0; i < NUM_PROTOCOL; i++) {
169
if (!pj_stricmp2(protocol, http_protocol_names[i])) {
170
return (pj_uint16_t)http_default_port[i];
176
static const char * get_protocol(const pj_str_t *protocol)
180
for (i = 0; i < NUM_PROTOCOL; i++) {
181
if (!pj_stricmp2(protocol, http_protocol_names[i])) {
182
return http_protocol_names[i];
186
/* Should not happen */
192
/* Syntax error handler for parser. */
193
static void on_syntax_error(pj_scanner *scanner)
195
PJ_UNUSED_ARG(scanner);
196
PJ_THROW(PJ_EINVAL); // syntax error
199
/* Callback when connection is established to the server */
200
static pj_bool_t http_on_connect(pj_activesock_t *asock,
203
pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock);
205
if (hreq->state == ABORTING || hreq->state == IDLE)
208
if (status != PJ_SUCCESS) {
209
hreq->error = status;
210
pj_http_req_cancel(hreq, PJ_TRUE);
214
/* OK, we are connected. Start sending the request */
215
hreq->state = SENDING_REQUEST;
216
http_req_start_sending(hreq);
220
static pj_bool_t http_on_data_sent(pj_activesock_t *asock,
221
pj_ioqueue_op_key_t *op_key,
224
pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock);
226
PJ_UNUSED_ARG(op_key);
228
if (hreq->state == ABORTING || hreq->state == IDLE)
232
hreq->error = (sent < 0 ? -sent : PJLIB_UTIL_EHTTPLOST);
233
pj_http_req_cancel(hreq, PJ_TRUE);
237
hreq->tcp_state.current_send_size += sent;
238
TRACE_((THIS_FILE, "\nData sent: %d out of %d bytes",
239
hreq->tcp_state.current_send_size, hreq->tcp_state.send_size));
240
if (hreq->tcp_state.current_send_size == hreq->tcp_state.send_size) {
241
/* Find out whether there is a request body to send. */
242
if (hreq->param.reqdata.total_size > 0 ||
243
hreq->param.reqdata.size > 0)
245
if (hreq->state == SENDING_REQUEST) {
246
/* Start sending the request body */
247
hreq->state = SENDING_REQUEST_BODY;
248
hreq->tcp_state.tot_chunk_size = 0;
249
pj_assert(hreq->param.reqdata.total_size == 0 ||
250
(hreq->param.reqdata.total_size > 0 &&
251
hreq->param.reqdata.size == 0));
253
/* Continue sending the next chunk of the request body */
254
hreq->tcp_state.tot_chunk_size += hreq->tcp_state.send_size;
255
if (hreq->tcp_state.tot_chunk_size ==
256
hreq->param.reqdata.total_size ||
257
hreq->param.reqdata.total_size == 0)
259
/* Finish sending all the chunks, start reading
262
hreq->state = REQUEST_SENT;
263
http_req_start_reading(hreq);
267
if (hreq->param.reqdata.total_size > 0 &&
268
hreq->cb.on_send_data)
270
/* Call the callback for the application to provide
271
* the next chunk of data to be sent.
273
(*hreq->cb.on_send_data)(hreq, &hreq->param.reqdata.data,
274
&hreq->param.reqdata.size);
275
/* Make sure the total data size given by the user does not
276
* exceed what the user originally said.
278
pj_assert(hreq->tcp_state.tot_chunk_size +
279
hreq->param.reqdata.size <=
280
hreq->param.reqdata.total_size);
282
http_req_start_sending(hreq);
284
/* No request body, proceed to reading the server's response. */
285
hreq->state = REQUEST_SENT;
286
http_req_start_reading(hreq);
292
static pj_bool_t http_on_data_read(pj_activesock_t *asock,
296
pj_size_t *remainder)
298
pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock);
300
TRACE_((THIS_FILE, "\nData received: %d bytes", size));
302
if (hreq->state == ABORTING || hreq->state == IDLE)
305
if (hreq->state == READING_RESPONSE) {
309
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
310
hreq->error = status;
311
pj_http_req_cancel(hreq, PJ_TRUE);
315
/* Parse the response. */
316
st = http_response_parse(hreq->pool, &hreq->response,
318
if (st == PJLIB_UTIL_EHTTPINCHDR) {
319
/* If we already use up all our buffer and still
320
* hasn't received the whole header, return error
322
if (size == BUF_SIZE) {
323
hreq->error = PJ_ETOOBIG; // response header size is too big
324
pj_http_req_cancel(hreq, PJ_TRUE);
327
/* Keep the data if we do not get the whole response header */
330
hreq->state = READING_DATA;
331
if (st != PJ_SUCCESS) {
332
/* Server replied with an invalid (or unknown) response
333
* format. We'll just pass the whole (unparsed) response
336
hreq->response.data = data;
337
hreq->response.size = size - rem;
340
/* If code is 401 or 407, find and parse WWW-Authenticate or
341
* Proxy-Authenticate header
343
if (hreq->response.status_code == 401 ||
344
hreq->response.status_code == 407)
346
const pj_str_t STR_WWW_AUTH = { "WWW-Authenticate", 16 };
347
const pj_str_t STR_PROXY_AUTH = { "Proxy-Authenticate", 18 };
348
pj_http_resp *response = &hreq->response;
349
pj_http_headers *hdrs = &response->headers;
352
status = PJ_ENOTFOUND;
353
for (i = 0; i < hdrs->count; i++) {
354
if (!pj_stricmp(&hdrs->header[i].name, &STR_WWW_AUTH) ||
355
!pj_stricmp(&hdrs->header[i].name, &STR_PROXY_AUTH))
357
status = parse_auth_chal(hreq->pool,
358
&hdrs->header[i].value,
359
&response->auth_chal);
364
/* Check if we should perform authentication */
365
if (status == PJ_SUCCESS &&
366
hreq->auth_state == AUTH_NONE &&
367
hreq->response.auth_chal.scheme.slen &&
368
hreq->param.auth_cred.username.slen &&
369
(hreq->param.auth_cred.scheme.slen == 0 ||
370
!pj_stricmp(&hreq->response.auth_chal.scheme,
371
&hreq->param.auth_cred.scheme)) &&
372
(hreq->param.auth_cred.realm.slen == 0 ||
373
!pj_stricmp(&hreq->response.auth_chal.realm,
374
&hreq->param.auth_cred.realm))
377
/* Yes, authentication is required and we have been
378
* configured with credential.
380
restart_req_with_auth(hreq);
381
if (hreq->auth_state == AUTH_RETRYING) {
382
/* We'll be resending the request with auth. This
383
* connection has been closed.
390
/* We already received the response header, call the
391
* appropriate callback.
393
if (hreq->cb.on_response)
394
(*hreq->cb.on_response)(hreq, &hreq->response);
395
hreq->response.data = NULL;
396
hreq->response.size = 0;
398
if (rem > 0 || hreq->response.content_length == 0)
399
return http_on_data_read(asock, (rem == 0 ? NULL:
400
(char *)data + size - rem),
401
rem, PJ_SUCCESS, NULL);
407
if (hreq->state != READING_DATA)
409
if (hreq->cb.on_data_read) {
410
/* If application wishes to receive the data once available, call
414
(*hreq->cb.on_data_read)(hreq, data, size);
416
if (hreq->response.size == 0) {
417
/* If we know the content length, allocate the data based
418
* on that, otherwise we'll use initial buffer size and grow
419
* it later if necessary.
421
hreq->response.size = (hreq->response.content_length == -1 ?
422
INITIAL_DATA_BUF_SIZE :
423
hreq->response.content_length);
424
hreq->response.data = pj_pool_alloc(hreq->pool,
425
hreq->response.size);
428
/* If the size of data received exceeds its current size,
429
* grow the buffer by a factor of 2.
431
if (hreq->tcp_state.current_read_size + size >
434
void *olddata = hreq->response.data;
436
hreq->response.data = pj_pool_alloc(hreq->pool,
437
hreq->response.size << 1);
438
pj_memcpy(hreq->response.data, olddata, hreq->response.size);
439
hreq->response.size <<= 1;
442
/* Append the response data. */
443
pj_memcpy((char *)hreq->response.data +
444
hreq->tcp_state.current_read_size, data, size);
446
hreq->tcp_state.current_read_size += size;
448
/* If the total data received so far is equal to the content length
449
* or if it's already EOF.
451
if ((hreq->response.content_length >=0 &&
452
(pj_ssize_t)hreq->tcp_state.current_read_size >=
453
hreq->response.content_length) ||
454
(status == PJ_EEOF && hreq->response.content_length == -1))
457
http_req_end_request(hreq);
458
hreq->response.size = hreq->tcp_state.current_read_size;
460
/* HTTP request is completed, call the callback. */
461
if (hreq->cb.on_complete) {
462
(*hreq->cb.on_complete)(hreq, PJ_SUCCESS, &hreq->response);
468
/* Error status or premature EOF. */
469
if ((status != PJ_SUCCESS && status != PJ_EPENDING && status != PJ_EEOF)
470
|| (status == PJ_EEOF && hreq->response.content_length > -1))
472
hreq->error = status;
473
pj_http_req_cancel(hreq, PJ_TRUE);
480
/* Callback to be called when query has timed out */
481
static void on_timeout( pj_timer_heap_t *timer_heap,
482
struct pj_timer_entry *entry)
484
pj_http_req *hreq = (pj_http_req *) entry->user_data;
486
PJ_UNUSED_ARG(timer_heap);
488
/* Recheck that the request is still not completed, since there is a
489
* slight possibility of race condition (timer elapsed while at the
490
* same time response arrives).
492
if (hreq->state == READING_COMPLETE) {
493
/* Yeah, we finish on time */
498
hreq->timer_entry.id = 0;
500
/* Request timed out. */
501
hreq->error = PJ_ETIMEDOUT;
502
pj_http_req_cancel(hreq, PJ_TRUE);
505
/* Parse authentication challenge */
506
static pj_status_t parse_auth_chal(pj_pool_t *pool, pj_str_t *input,
507
pj_http_auth_chal *chal)
510
const pj_str_t REALM_STR = { "realm", 5},
511
NONCE_STR = { "nonce", 5},
512
ALGORITHM_STR = { "algorithm", 9 },
513
STALE_STR = { "stale", 5},
514
QOP_STR = { "qop", 3},
515
OPAQUE_STR = { "opaque", 6};
516
pj_status_t status = PJ_SUCCESS;
519
pj_scan_init(&scanner, input->ptr, input->slen, PJ_SCAN_AUTOSKIP_WS,
522
/* Get auth scheme */
523
if (*scanner.curptr == '"') {
524
pj_scan_get_quote(&scanner, '"', '"', &chal->scheme);
526
chal->scheme.slen -= 2;
528
pj_scan_get_until_chr(&scanner, " \t\r\n", &chal->scheme);
531
/* Loop parsing all parameters */
533
const char *end_param = ", \t\r\n;";
534
pj_str_t name, value;
536
/* Get pair of parameter name and value */
539
pj_scan_get_until_chr(&scanner, "=, \t\r\n", &name);
540
if (*scanner.curptr == '=') {
541
pj_scan_get_char(&scanner);
542
if (!pj_scan_is_eof(&scanner)) {
543
if (*scanner.curptr == '"' || *scanner.curptr == '\'') {
544
int quote_char = *scanner.curptr;
545
pj_scan_get_quote(&scanner, quote_char, quote_char,
549
} else if (!strchr(end_param, *scanner.curptr)) {
550
pj_scan_get_until_chr(&scanner, end_param, &value);
553
value = pj_str_unescape(pool, &value);
556
if (!pj_stricmp(&name, &REALM_STR)) {
559
} else if (!pj_stricmp(&name, &NONCE_STR)) {
562
} else if (!pj_stricmp(&name, &ALGORITHM_STR)) {
563
chal->algorithm = value;
565
} else if (!pj_stricmp(&name, &OPAQUE_STR)) {
566
chal->opaque = value;
568
} else if (!pj_stricmp(&name, &QOP_STR)) {
571
} else if (!pj_stricmp(&name, &STALE_STR)) {
572
chal->stale = value.slen &&
573
(*value.ptr != '0') &&
574
(*value.ptr != 'f') &&
580
if (!pj_scan_is_eof(&scanner) && *scanner.curptr == ',')
581
pj_scan_get_char(&scanner);
588
status = PJ_GET_EXCEPTION();
589
pj_bzero(chal, sizeof(*chal));
590
TRACE_((THIS_FILE, "Error: parsing of auth header failed"));
593
pj_scan_fini(&scanner);
597
/* The same as #pj_http_headers_add_elmt() with char * as
600
PJ_DEF(pj_status_t) pj_http_headers_add_elmt2(pj_http_headers *headers,
601
char *name, char *val)
606
return pj_http_headers_add_elmt(headers, &f, &v);
609
PJ_DEF(pj_status_t) pj_http_headers_add_elmt(pj_http_headers *headers,
613
PJ_ASSERT_RETURN(headers && name && val, PJ_FALSE);
614
if (headers->count >= PJ_HTTP_HEADER_SIZE)
616
pj_strassign(&headers->header[headers->count].name, name);
617
pj_strassign(&headers->header[headers->count++].value, val);
621
static pj_status_t http_response_parse(pj_pool_t *pool,
622
pj_http_resp *response,
623
void *data, pj_size_t size,
624
pj_size_t *remainder)
628
char *end_status, *newdata;
631
const pj_str_t STR_CONTENT_LENGTH = { CONTENT_LENGTH, 14 };
636
PJ_ASSERT_RETURN(response, PJ_EINVAL);
638
return PJLIB_UTIL_EHTTPINCHDR;
639
/* Detect whether we already receive the response's status-line
640
* and its headers. We're looking for a pair of CRLFs. A pair of
641
* LFs is also supported although it is not RFC standard.
644
for (i = 1, cptr++; i < size; i++, cptr++) {
646
if (*(cptr - 1) == '\n')
648
if (*(cptr - 1) == '\r') {
649
if (i >= 3 && *(cptr - 2) == '\n' && *(cptr - 3) == '\r')
655
return PJLIB_UTIL_EHTTPINCHDR;
656
*remainder = size - 1 - i;
658
pj_bzero(response, sizeof(response));
659
response->content_length = -1;
661
newdata = (char*) pj_pool_alloc(pool, i);
662
pj_memcpy(newdata, data, i);
664
/* Parse the status-line. */
665
pj_scan_init(&scanner, newdata, i, 0, &on_syntax_error);
667
pj_scan_get_until_ch(&scanner, ' ', &response->version);
668
pj_scan_advance_n(&scanner, 1, PJ_FALSE);
669
pj_scan_get_until_ch(&scanner, ' ', &s);
670
response->status_code = (pj_uint16_t)pj_strtoul(&s);
671
pj_scan_advance_n(&scanner, 1, PJ_FALSE);
672
pj_scan_get_until_ch(&scanner, '\n', &response->reason);
673
if (response->reason.ptr[response->reason.slen-1] == '\r')
674
response->reason.slen--;
677
pj_scan_fini(&scanner);
678
return PJ_GET_EXCEPTION();
682
end_status = scanner.curptr;
683
pj_scan_fini(&scanner);
685
/* Parse the response headers. */
686
size = i - 2 - (end_status - newdata);
688
status = http_headers_parse(end_status + 1, size,
694
/* Find content-length header field. */
695
for (i = 0; i < response->headers.count; i++) {
696
if (!pj_stricmp(&response->headers.header[i].name,
697
&STR_CONTENT_LENGTH))
699
response->content_length =
700
pj_strtoul(&response->headers.header[i].value);
701
/* If content length is zero, make sure that it is because the
702
* header value is really zero and not due to parsing error.
704
if (response->content_length == 0) {
705
if (pj_strcmp2(&response->headers.header[i].value, "0")) {
706
response->content_length = -1;
716
static pj_status_t http_headers_parse(char *hdata, pj_size_t size,
717
pj_http_headers *headers)
724
PJ_ASSERT_RETURN(headers, PJ_EINVAL);
726
pj_scan_init(&scanner, hdata, size, 0, &on_syntax_error);
728
/* Parse each line of header field consisting of header field name and
729
* value, separated by ":" and any number of white spaces.
733
pj_scan_get_until_chr(&scanner, ":\n", &s);
734
if (*scanner.curptr == ':') {
735
pj_scan_advance_n(&scanner, 1, PJ_TRUE);
736
pj_scan_get_until_ch(&scanner, '\n', &s2);
737
if (s2.ptr[s2.slen-1] == '\r')
739
status = pj_http_headers_add_elmt(headers, &s, &s2);
740
if (status != PJ_SUCCESS)
743
pj_scan_advance_n(&scanner, 1, PJ_TRUE);
745
if (pj_scan_is_eof(&scanner))
750
pj_scan_fini(&scanner);
751
return PJ_GET_EXCEPTION();
755
pj_scan_fini(&scanner);
760
PJ_DEF(void) pj_http_req_param_default(pj_http_req_param *param)
763
pj_bzero(param, sizeof(*param));
764
param->addr_family = pj_AF_INET();
765
pj_strset2(¶m->method, (char*)http_method_names[HTTP_GET]);
766
pj_strset2(¶m->version, (char*)HTTP_1_0);
767
param->timeout.msec = PJ_HTTP_DEFAULT_TIMEOUT;
768
pj_time_val_normalize(¶m->timeout);
769
param->max_retries = 3;
772
/* Get the location of '@' character to indicate the end of
773
* user:passwd part of an URI. If user:passwd part is not
774
* present, NULL will be returned.
776
static char *get_url_at_pos(const char *str, long len)
778
const char *end = str + len;
782
while (p!=end && *p!='/') ++p;
783
if (p!=end && *p=='/') ++p;
784
if (p!=end && *p=='/') ++p;
785
if (p==end) return NULL;
787
for (; p!=end; ++p) {
800
PJ_DEF(pj_status_t) pj_http_req_parse_url(const pj_str_t *url,
809
pj_bzero(hurl, sizeof(*hurl));
810
pj_scan_init(&scanner, url->ptr, url->slen, 0, &on_syntax_error);
815
/* Exhaust any whitespaces. */
816
pj_scan_skip_whitespace(&scanner);
818
/* Parse the protocol */
819
pj_scan_get_until_ch(&scanner, ':', &s);
820
if (!pj_stricmp2(&s, http_protocol_names[PROTOCOL_HTTP])) {
821
pj_strset2(&hurl->protocol,
822
(char*)http_protocol_names[PROTOCOL_HTTP]);
823
} else if (!pj_stricmp2(&s, http_protocol_names[PROTOCOL_HTTPS])) {
824
pj_strset2(&hurl->protocol,
825
(char*)http_protocol_names[PROTOCOL_HTTPS]);
827
PJ_THROW(PJ_ENOTSUP); // unsupported protocol
830
if (pj_scan_strcmp(&scanner, "://", 3)) {
831
PJ_THROW(PJLIB_UTIL_EHTTPINURL); // no "://" after protocol name
833
pj_scan_advance_n(&scanner, 3, PJ_FALSE);
835
if (get_url_at_pos(url->ptr, url->slen)) {
836
/* Parse username and password */
837
pj_scan_get_until_chr(&scanner, ":@", &hurl->username);
838
if (*scanner.curptr == ':') {
839
pj_scan_get_char(&scanner);
840
pj_scan_get_until_chr(&scanner, "@", &hurl->passwd);
842
hurl->passwd.slen = 0;
844
pj_scan_get_char(&scanner);
847
/* Parse the host and port number (if any) */
848
pj_scan_get_until_chr(&scanner, ":/", &s);
849
pj_strassign(&hurl->host, &s);
850
if (hurl->host.slen==0)
852
if (pj_scan_is_eof(&scanner) || *scanner.curptr == '/') {
853
/* No port number specified */
854
/* Assume default http/https port number */
855
hurl->port = get_http_default_port(&hurl->protocol);
856
pj_assert(hurl->port > 0);
858
pj_scan_advance_n(&scanner, 1, PJ_FALSE);
859
pj_scan_get_until_ch(&scanner, '/', &s);
860
/* Parse the port number */
861
hurl->port = (pj_uint16_t)pj_strtoul(&s);
863
PJ_THROW(PJLIB_UTIL_EHTTPINPORT); // invalid port number
866
if (!pj_scan_is_eof(&scanner)) {
867
hurl->path.ptr = scanner.curptr;
868
hurl->path.slen = scanner.end - scanner.curptr;
870
/* no path, append '/' */
871
pj_cstr(&hurl->path, "/");
875
pj_scan_fini(&scanner);
876
return PJ_GET_EXCEPTION();
880
pj_scan_fini(&scanner);
884
PJ_DEF(void) pj_http_req_set_timeout(pj_http_req *http_req,
885
const pj_time_val* timeout)
887
pj_memcpy(&http_req->param.timeout, timeout, sizeof(*timeout));
890
PJ_DEF(pj_status_t) pj_http_req_create(pj_pool_t *pool,
892
pj_timer_heap_t *timer,
893
pj_ioqueue_t *ioqueue,
894
const pj_http_req_param *param,
895
const pj_http_req_callback *hcb,
896
pj_http_req **http_req)
903
PJ_ASSERT_RETURN(pool && url && timer && ioqueue &&
904
hcb && http_req, PJ_EINVAL);
907
own_pool = pj_pool_create(pool->factory, NULL, INITIAL_POOL_SIZE,
908
POOL_INCREMENT_SIZE, NULL);
909
hreq = PJ_POOL_ZALLOC_T(own_pool, struct pj_http_req);
914
hreq->pool = own_pool;
915
hreq->ioqueue = ioqueue;
918
pj_memcpy(&hreq->cb, hcb, sizeof(*hcb));
920
hreq->resolved = PJ_FALSE;
921
hreq->buffer.ptr = NULL;
922
pj_timer_entry_init(&hreq->timer_entry, 0, hreq, &on_timeout);
924
/* Initialize parameter */
926
pj_memcpy(&hreq->param, param, sizeof(*param));
927
/* TODO: validate the param here
928
* Should we validate the method as well? If yes, based on all HTTP
929
* methods or based on supported methods only? For the later, one
930
* drawback would be that you can't use this if the method is not
931
* officially supported
933
PJ_ASSERT_RETURN(hreq->param.addr_family==PJ_AF_UNSPEC ||
934
hreq->param.addr_family==PJ_AF_INET ||
935
hreq->param.addr_family==PJ_AF_INET6, PJ_EAFNOTSUP);
936
PJ_ASSERT_RETURN(!pj_strcmp2(&hreq->param.version, HTTP_1_0) ||
937
!pj_strcmp2(&hreq->param.version, HTTP_1_1),
939
pj_time_val_normalize(&hreq->param.timeout);
941
pj_http_req_param_default(&hreq->param);
945
if (!pj_strdup_with_null(hreq->pool, &hreq->url, url)) {
946
pj_pool_release(hreq->pool);
949
status = pj_http_req_parse_url(&hreq->url, &hreq->hurl);
950
if (status != PJ_SUCCESS) {
951
pj_pool_release(hreq->pool);
952
return status; // Invalid URL supplied
955
/* If URL contains username/password, move them to credential and
956
* remove them from the URL.
958
if ((at_pos=get_url_at_pos(hreq->url.ptr, hreq->url.slen)) != NULL) {
960
char *user_pos = pj_strchr(&hreq->url, '/');
963
/* Save credential first, unescape the string */
964
tmp = pj_str_unescape(hreq->pool, &hreq->hurl.username);;
965
pj_strdup(hreq->pool, &hreq->param.auth_cred.username, &tmp);
967
tmp = pj_str_unescape(hreq->pool, &hreq->hurl.passwd);
968
pj_strdup(hreq->pool, &hreq->param.auth_cred.data, &tmp);
970
hreq->hurl.username.ptr = hreq->hurl.passwd.ptr = NULL;
971
hreq->hurl.username.slen = hreq->hurl.passwd.slen = 0;
973
/* Remove "username:password@" from the URL */
974
pj_assert(user_pos != 0 && user_pos < at_pos);
976
removed_len = at_pos + 1 - user_pos;
977
pj_memmove(user_pos, at_pos+1, hreq->url.ptr+hreq->url.slen-at_pos-1);
978
hreq->url.slen -= removed_len;
980
/* Need to adjust hostname and path pointers due to memmove*/
981
if (hreq->hurl.host.ptr > user_pos &&
982
hreq->hurl.host.ptr < user_pos + hreq->url.slen)
984
hreq->hurl.host.ptr -= removed_len;
986
/* path may come from a string constant, don't shift it if so */
987
if (hreq->hurl.path.ptr > user_pos &&
988
hreq->hurl.path.ptr < user_pos + hreq->url.slen)
990
hreq->hurl.path.ptr -= removed_len;
998
PJ_DEF(pj_bool_t) pj_http_req_is_running(const pj_http_req *http_req)
1000
PJ_ASSERT_RETURN(http_req, PJ_FALSE);
1001
return (http_req->state != IDLE);
1004
PJ_DEF(void*) pj_http_req_get_user_data(pj_http_req *http_req)
1006
PJ_ASSERT_RETURN(http_req, NULL);
1007
return http_req->param.user_data;
1010
static pj_status_t start_http_req(pj_http_req *http_req,
1011
pj_bool_t notify_on_fail)
1013
pj_sock_t sock = PJ_INVALID_SOCKET;
1015
pj_activesock_cb asock_cb;
1018
PJ_ASSERT_RETURN(http_req, PJ_EINVAL);
1019
/* Http request is not idle, a request was initiated before and
1020
* is still in progress
1022
PJ_ASSERT_RETURN(http_req->state == IDLE, PJ_EBUSY);
1024
/* Reset few things to make sure restarting works */
1025
http_req->error = 0;
1026
http_req->response.headers.count = 0;
1027
pj_bzero(&http_req->tcp_state, sizeof(http_req->tcp_state));
1029
if (!http_req->resolved) {
1030
/* Resolve the Internet address of the host */
1031
status = pj_sockaddr_init(http_req->param.addr_family,
1032
&http_req->addr, &http_req->hurl.host,
1033
http_req->hurl.port);
1034
if (status != PJ_SUCCESS ||
1035
!pj_sockaddr_has_addr(&http_req->addr) ||
1036
(http_req->param.addr_family==pj_AF_INET() &&
1037
http_req->addr.ipv4.sin_addr.s_addr==PJ_INADDR_NONE))
1041
http_req->resolved = PJ_TRUE;
1044
status = pj_sock_socket(http_req->param.addr_family, pj_SOCK_STREAM(),
1046
if (status != PJ_SUCCESS)
1047
goto on_return; // error creating socket
1049
pj_bzero(&asock_cb, sizeof(asock_cb));
1050
asock_cb.on_data_read = &http_on_data_read;
1051
asock_cb.on_data_sent = &http_on_data_sent;
1052
asock_cb.on_connect_complete = &http_on_connect;
1056
pj_sockaddr_in bound_addr;
1057
pj_uint16_t port = 0;
1059
/* If we are using port restriction.
1060
* Get a random port within the range
1062
if (http_req->param.source_port_range_start != 0) {
1063
port = (pj_uint16_t)
1064
(http_req->param.source_port_range_start +
1065
(pj_rand() % http_req->param.source_port_range_size));
1068
pj_sockaddr_in_init(&bound_addr, NULL, port);
1069
status = pj_sock_bind(sock, &bound_addr, sizeof(bound_addr));
1071
} while (status != PJ_SUCCESS && (retry++ < http_req->param.max_retries));
1073
if (status != PJ_SUCCESS) {
1074
PJ_PERROR(1,(THIS_FILE, status,
1075
"Unable to bind to the requested port"));
1076
pj_sock_close(sock);
1080
// TODO: should we set whole data to 0 by default?
1081
// or add it in the param?
1082
status = pj_activesock_create(http_req->pool, sock, pj_SOCK_STREAM(),
1083
NULL, http_req->ioqueue,
1084
&asock_cb, http_req, &http_req->asock);
1085
if (status != PJ_SUCCESS) {
1086
pj_sock_close(sock);
1087
goto on_return; // error creating activesock
1090
/* Schedule timeout timer for the request */
1091
pj_assert(http_req->timer_entry.id == 0);
1092
http_req->timer_entry.id = 1;
1093
status = pj_timer_heap_schedule(http_req->timer, &http_req->timer_entry,
1094
&http_req->param.timeout);
1095
if (status != PJ_SUCCESS) {
1096
http_req->timer_entry.id = 0;
1097
goto on_return; // error scheduling timer
1100
/* Connect to host */
1101
http_req->state = CONNECTING;
1102
status = pj_activesock_start_connect(http_req->asock, http_req->pool,
1103
(pj_sock_t *)&(http_req->addr),
1104
pj_sockaddr_get_len(&http_req->addr));
1105
if (status == PJ_SUCCESS) {
1106
http_req->state = SENDING_REQUEST;
1107
status = http_req_start_sending(http_req);
1108
if (status != PJ_SUCCESS)
1110
} else if (status != PJ_EPENDING) {
1111
goto on_return; // error connecting
1117
http_req->error = status;
1119
pj_http_req_cancel(http_req, PJ_TRUE);
1121
http_req_end_request(http_req);
1126
/* Starts an asynchronous HTTP request to the URL specified. */
1127
PJ_DEF(pj_status_t) pj_http_req_start(pj_http_req *http_req)
1129
return start_http_req(http_req, PJ_FALSE);
1132
/* Respond to basic authentication challenge */
1133
static pj_status_t auth_respond_basic(pj_http_req *hreq)
1135
/* Basic authentication:
1136
* credentials = "Basic" basic-credentials
1137
* basic-credentials = base64-user-pass
1138
* base64-user-pass = <base64 [4] encoding of user-pass>
1139
* user-pass = userid ":" password
1142
* Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
1145
pj_http_header_elmt *phdr;
1148
/* Use send buffer to store userid ":" password */
1149
user_pass.ptr = hreq->buffer.ptr;
1150
pj_strcpy(&user_pass, &hreq->param.auth_cred.username);
1151
pj_strcat2(&user_pass, ":");
1152
pj_strcat(&user_pass, &hreq->param.auth_cred.data);
1154
/* Create Authorization header */
1155
phdr = &hreq->param.headers.header[hreq->param.headers.count++];
1156
pj_bzero(phdr, sizeof(*phdr));
1157
if (hreq->response.status_code == 401)
1158
phdr->name = pj_str("Authorization");
1160
phdr->name = pj_str("Proxy-Authorization");
1162
len = PJ_BASE256_TO_BASE64_LEN(user_pass.slen) + 10;
1163
phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len);
1164
phdr->value.slen = 0;
1166
pj_strcpy2(&phdr->value, "Basic ");
1167
len -= phdr->value.slen;
1168
pj_base64_encode((pj_uint8_t*)user_pass.ptr, (int)user_pass.slen,
1169
phdr->value.ptr + phdr->value.slen, &len);
1170
phdr->value.slen += len;
1175
/** Length of digest string. */
1176
#define MD5_STRLEN 32
1177
/* A macro just to get rid of type mismatch between char and unsigned char */
1178
#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len)
1180
/* Transform digest to string.
1181
* output must be at least PJSIP_MD5STRLEN+1 bytes.
1183
* NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
1185
static void digest2str(const unsigned char digest[], char *output)
1188
for (i = 0; i<16; ++i) {
1189
pj_val_to_hex_digit(digest[i], output);
1194
static void auth_create_digest_response(pj_str_t *result,
1195
const pj_http_auth_cred *cred,
1196
const pj_str_t *nonce,
1198
const pj_str_t *cnonce,
1199
const pj_str_t *qop,
1200
const pj_str_t *uri,
1201
const pj_str_t *realm,
1202
const pj_str_t *method)
1204
char ha1[MD5_STRLEN];
1205
char ha2[MD5_STRLEN];
1206
unsigned char digest[16];
1209
pj_assert(result->slen >= MD5_STRLEN);
1211
TRACE_((THIS_FILE, "Begin creating digest"));
1213
if (cred->data_type == 0) {
1215
*** ha1 = MD5(username ":" realm ":" password)
1218
MD5_APPEND( &pms, cred->username.ptr, cred->username.slen);
1219
MD5_APPEND( &pms, ":", 1);
1220
MD5_APPEND( &pms, realm->ptr, realm->slen);
1221
MD5_APPEND( &pms, ":", 1);
1222
MD5_APPEND( &pms, cred->data.ptr, cred->data.slen);
1223
pj_md5_final(&pms, digest);
1225
digest2str(digest, ha1);
1227
} else if (cred->data_type == 1) {
1228
pj_assert(cred->data.slen == 32);
1229
pj_memcpy( ha1, cred->data.ptr, cred->data.slen );
1231
pj_assert(!"Invalid data_type");
1234
TRACE_((THIS_FILE, " ha1=%.32s", ha1));
1237
*** ha2 = MD5(method ":" req_uri)
1240
MD5_APPEND( &pms, method->ptr, method->slen);
1241
MD5_APPEND( &pms, ":", 1);
1242
MD5_APPEND( &pms, uri->ptr, uri->slen);
1243
pj_md5_final(&pms, digest);
1244
digest2str(digest, ha2);
1246
TRACE_((THIS_FILE, " ha2=%.32s", ha2));
1249
*** When qop is not used:
1250
*** response = MD5(ha1 ":" nonce ":" ha2)
1252
*** When qop=auth is used:
1253
*** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
1256
MD5_APPEND( &pms, ha1, MD5_STRLEN);
1257
MD5_APPEND( &pms, ":", 1);
1258
MD5_APPEND( &pms, nonce->ptr, nonce->slen);
1259
if (qop && qop->slen != 0) {
1260
MD5_APPEND( &pms, ":", 1);
1261
MD5_APPEND( &pms, nc->ptr, nc->slen);
1262
MD5_APPEND( &pms, ":", 1);
1263
MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
1264
MD5_APPEND( &pms, ":", 1);
1265
MD5_APPEND( &pms, qop->ptr, qop->slen);
1267
MD5_APPEND( &pms, ":", 1);
1268
MD5_APPEND( &pms, ha2, MD5_STRLEN);
1270
/* This is the final response digest. */
1271
pj_md5_final(&pms, digest);
1273
/* Convert digest to string and store in chal->response. */
1274
result->slen = MD5_STRLEN;
1275
digest2str(digest, result->ptr);
1277
TRACE_((THIS_FILE, " digest=%.32s", result->ptr));
1278
TRACE_((THIS_FILE, "Digest created"));
1281
/* Find out if qop offer contains "auth" token */
1282
static pj_bool_t auth_has_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
1287
pj_strdup_with_null( pool, &qop, qop_offer);
1290
*p = (char)pj_tolower(*p);
1296
if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
1298
if (e=='"' || e==',' || e==0)
1310
#define STR_PREC(s) (int)(s).slen, (s).ptr
1312
/* Respond to digest authentication */
1313
static pj_status_t auth_respond_digest(pj_http_req *hreq)
1315
const pj_http_auth_chal *chal = &hreq->response.auth_chal;
1316
const pj_http_auth_cred *cred = &hreq->param.auth_cred;
1317
pj_http_header_elmt *phdr;
1318
char digest_response_buf[MD5_STRLEN];
1320
pj_str_t digest_response;
1322
/* Check algorithm is supported. We only support MD5 */
1323
if (chal->algorithm.slen!=0 &&
1324
pj_stricmp2(&chal->algorithm, "MD5"))
1326
TRACE_((THIS_FILE, "Error: Unsupported digest algorithm \"%.*s\"",
1327
chal->algorithm.slen, chal->algorithm.ptr));
1331
/* Add Authorization header */
1332
phdr = &hreq->param.headers.header[hreq->param.headers.count++];
1333
pj_bzero(phdr, sizeof(*phdr));
1334
if (hreq->response.status_code == 401)
1335
phdr->name = pj_str("Authorization");
1337
phdr->name = pj_str("Proxy-Authorization");
1339
/* Allocate space for the header */
1340
len = 8 + /* Digest */
1341
16 + hreq->param.auth_cred.username.slen + /* username= */
1342
12 + chal->realm.slen + /* realm= */
1343
12 + chal->nonce.slen + /* nonce= */
1344
8 + hreq->hurl.path.slen + /* uri= */
1345
16 + /* algorithm=MD5 */
1346
16 + MD5_STRLEN + /* response= */
1350
12 + chal->opaque.slen + /* opaque=".." */
1352
phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len);
1354
/* Configure buffer to temporarily store the digest */
1355
digest_response.ptr = digest_response_buf;
1356
digest_response.slen = MD5_STRLEN;
1358
if (chal->qop.slen == 0) {
1359
const pj_str_t STR_MD5 = { "MD5", 3 };
1361
/* Server doesn't require quality of protection. */
1362
auth_create_digest_response(&digest_response, cred,
1363
&chal->nonce, NULL, NULL, NULL,
1364
&hreq->hurl.path, &chal->realm,
1365
&hreq->param.method);
1367
len = pj_ansi_snprintf(
1368
phdr->value.ptr, len,
1369
"Digest username=\"%.*s\", "
1374
"response=\"%.*s\"",
1375
STR_PREC(cred->username),
1376
STR_PREC(chal->realm),
1377
STR_PREC(chal->nonce),
1378
STR_PREC(hreq->hurl.path),
1380
STR_PREC(digest_response));
1382
return PJ_ETOOSMALL;
1383
phdr->value.slen = len;
1385
} else if (auth_has_qop(hreq->pool, &chal->qop)) {
1386
/* Server requires quality of protection.
1387
* We respond with selecting "qop=auth" protection.
1389
const pj_str_t STR_MD5 = { "MD5", 3 };
1390
const pj_str_t qop = pj_str("auth");
1391
const pj_str_t nc = pj_str("00000001");
1392
const pj_str_t cnonce = pj_str("b39971");
1394
auth_create_digest_response(&digest_response, cred,
1395
&chal->nonce, &nc, &cnonce, &qop,
1396
&hreq->hurl.path, &chal->realm,
1397
&hreq->param.method);
1398
len = pj_ansi_snprintf(
1399
phdr->value.ptr, len,
1400
"Digest username=\"%.*s\", "
1405
"response=\"%.*s\", "
1409
STR_PREC(cred->username),
1410
STR_PREC(chal->realm),
1411
STR_PREC(chal->nonce),
1412
STR_PREC(hreq->hurl.path),
1414
STR_PREC(digest_response),
1419
return PJ_ETOOSMALL;
1420
phdr->value.slen = len;
1422
if (chal->opaque.slen) {
1423
pj_strcat2(&phdr->value, ", opaque=\"");
1424
pj_strcat(&phdr->value, &chal->opaque);
1425
pj_strcat2(&phdr->value, "\"");
1429
/* Server requires quality protection that we don't support. */
1430
TRACE_((THIS_FILE, "Error: Unsupported qop offer %.*s",
1431
chal->qop.slen, chal->qop.ptr));
1439
static void restart_req_with_auth(pj_http_req *hreq)
1441
pj_http_auth_chal *chal = &hreq->response.auth_chal;
1442
pj_http_auth_cred *cred = &hreq->param.auth_cred;
1445
if (hreq->param.headers.count >= PJ_HTTP_HEADER_SIZE) {
1446
TRACE_((THIS_FILE, "Error: no place to put Authorization header"));
1447
hreq->auth_state = AUTH_DONE;
1451
/* If credential specifies specific scheme, make sure they match */
1452
if (cred->scheme.slen && pj_stricmp(&chal->scheme, &cred->scheme)) {
1453
status = PJ_ENOTSUP;
1454
TRACE_((THIS_FILE, "Error: auth schemes mismatch"));
1458
/* If credential specifies specific realm, make sure they match */
1459
if (cred->realm.slen && pj_stricmp(&chal->realm, &cred->realm)) {
1460
status = PJ_ENOTSUP;
1461
TRACE_((THIS_FILE, "Error: auth realms mismatch"));
1465
if (!pj_stricmp2(&chal->scheme, "basic")) {
1466
status = auth_respond_basic(hreq);
1467
} else if (!pj_stricmp2(&chal->scheme, "digest")) {
1468
status = auth_respond_digest(hreq);
1470
TRACE_((THIS_FILE, "Error: unsupported HTTP auth scheme"));
1471
status = PJ_ENOTSUP;
1474
if (status != PJ_SUCCESS)
1477
http_req_end_request(hreq);
1479
status = start_http_req(hreq, PJ_TRUE);
1480
if (status != PJ_SUCCESS)
1483
hreq->auth_state = AUTH_RETRYING;
1487
hreq->auth_state = AUTH_DONE;
1491
/* snprintf() to a pj_str_t struct with an option to append the
1492
* result at the back of the string.
1494
void str_snprintf(pj_str_t *s, size_t size,
1495
pj_bool_t append, const char *format, ...)
1500
va_start(arg, format);
1504
retval = pj_ansi_vsnprintf(s->ptr + s->slen,
1506
s->slen += ((retval < (int)size) ? retval : size - 1);
1510
static pj_status_t http_req_start_sending(pj_http_req *hreq)
1517
PJ_ASSERT_RETURN(hreq->state == SENDING_REQUEST ||
1518
hreq->state == SENDING_REQUEST_BODY, PJ_EBUG);
1520
if (hreq->state == SENDING_REQUEST) {
1521
/* Prepare the request data */
1522
if (!hreq->buffer.ptr)
1523
hreq->buffer.ptr = (char*)pj_pool_alloc(hreq->pool, BUF_SIZE);
1524
pj_strassign(&pkt, &hreq->buffer);
1527
str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "%.*s %.*s %s/%.*s\r\n",
1528
STR_PREC(hreq->param.method),
1529
STR_PREC(hreq->hurl.path),
1530
get_protocol(&hreq->hurl.protocol),
1531
STR_PREC(hreq->param.version));
1532
/* Header field "Host" */
1533
str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "Host: %.*s:%d\r\n",
1534
STR_PREC(hreq->hurl.host), hreq->hurl.port);
1535
if (!pj_strcmp2(&hreq->param.method, http_method_names[HTTP_PUT])) {
1538
/* Header field "Content-Length" */
1539
pj_utoa(hreq->param.reqdata.total_size ?
1540
hreq->param.reqdata.total_size:
1541
hreq->param.reqdata.size, buf);
1542
str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "%s: %s\r\n",
1543
CONTENT_LENGTH, buf);
1546
/* Append user-specified headers */
1547
for (i = 0; i < hreq->param.headers.count; i++) {
1548
str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "%.*s: %.*s\r\n",
1549
STR_PREC(hreq->param.headers.header[i].name),
1550
STR_PREC(hreq->param.headers.header[i].value));
1552
if (pkt.slen >= BUF_SIZE - 1) {
1553
status = PJLIB_UTIL_EHTTPINSBUF;
1557
pj_strcat2(&pkt, "\r\n");
1558
pkt.ptr[pkt.slen] = 0;
1559
TRACE_((THIS_FILE, "%s", pkt.ptr));
1561
pkt.ptr = (char*)hreq->param.reqdata.data;
1562
pkt.slen = hreq->param.reqdata.size;
1565
/* Send the request */
1566
len = pj_strlen(&pkt);
1567
pj_ioqueue_op_key_init(&hreq->op_key, sizeof(hreq->op_key));
1568
hreq->tcp_state.send_size = len;
1569
hreq->tcp_state.current_send_size = 0;
1570
status = pj_activesock_send(hreq->asock, &hreq->op_key,
1573
if (status == PJ_SUCCESS) {
1574
http_on_data_sent(hreq->asock, &hreq->op_key, len);
1575
} else if (status != PJ_EPENDING) {
1576
goto on_return; // error sending data
1582
http_req_end_request(hreq);
1586
static pj_status_t http_req_start_reading(pj_http_req *hreq)
1590
PJ_ASSERT_RETURN(hreq->state == REQUEST_SENT, PJ_EBUG);
1592
/* Receive the response */
1593
hreq->state = READING_RESPONSE;
1594
hreq->tcp_state.current_read_size = 0;
1595
pj_assert(hreq->buffer.ptr);
1596
status = pj_activesock_start_read2(hreq->asock, hreq->pool, BUF_SIZE,
1597
(void**)&hreq->buffer.ptr, 0);
1598
if (status != PJ_SUCCESS) {
1600
http_req_end_request(hreq);
1607
static pj_status_t http_req_end_request(pj_http_req *hreq)
1610
pj_activesock_close(hreq->asock);
1614
/* Cancel query timeout timer. */
1615
if (hreq->timer_entry.id != 0) {
1616
pj_timer_heap_cancel(hreq->timer, &hreq->timer_entry);
1617
/* Invalidate id. */
1618
hreq->timer_entry.id = 0;
1626
PJ_DEF(pj_status_t) pj_http_req_cancel(pj_http_req *http_req,
1629
http_req->state = ABORTING;
1631
http_req_end_request(http_req);
1633
if (notify && http_req->cb.on_complete) {
1634
(*http_req->cb.on_complete)(http_req, (!http_req->error?
1635
PJ_ECANCELLED: http_req->error), NULL);
1642
PJ_DEF(pj_status_t) pj_http_req_destroy(pj_http_req *http_req)
1644
PJ_ASSERT_RETURN(http_req, PJ_EINVAL);
1646
/* If there is any pending request, cancel it */
1647
if (http_req->state != IDLE) {
1648
pj_http_req_cancel(http_req, PJ_FALSE);
1651
pj_pool_release(http_req->pool);