~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjlib-util/src/pjlib-util/http_client.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: http_client.c 3841 2011-10-24 09:28:13Z ming $ */
2
 
/*
3
 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
 
 *
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.
9
 
 *
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.
14
 
 *
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
18
 
 */
19
 
 
20
 
#include <pjlib-util/http_client.h>
21
 
#include <pj/activesock.h>
22
 
#include <pj/assert.h>
23
 
#include <pj/ctype.h>
24
 
#include <pj/errno.h>
25
 
#include <pj/except.h>
26
 
#include <pj/pool.h>
27
 
#include <pj/string.h>
28
 
#include <pj/timer.h>
29
 
#include <pj/rand.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>
35
 
 
36
 
#define THIS_FILE   "http_client.c"
37
 
 
38
 
#if 0
39
 
    /* Enable some tracing */
40
 
    #define TRACE_(arg) PJ_LOG(3,arg)
41
 
#else
42
 
    #define TRACE_(arg)
43
 
#endif
44
 
 
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. */
50
 
#define BUF_SIZE                2048
51
 
/* Initial data buffer size to store the data in case content-
52
 
 * length is not specified in the server's response.
53
 
 */
54
 
#define INITIAL_DATA_BUF_SIZE   2048
55
 
#define INITIAL_POOL_SIZE       1024
56
 
#define POOL_INCREMENT_SIZE     512
57
 
 
58
 
enum http_protocol
59
 
{
60
 
    PROTOCOL_HTTP,
61
 
    PROTOCOL_HTTPS
62
 
};
63
 
 
64
 
static const char *http_protocol_names[NUM_PROTOCOL] =
65
 
{
66
 
    "HTTP",
67
 
    "HTTPS"
68
 
};
69
 
 
70
 
static const unsigned int http_default_port[NUM_PROTOCOL] =
71
 
{
72
 
    80,
73
 
    443
74
 
};
75
 
 
76
 
enum http_method
77
 
{
78
 
    HTTP_GET,
79
 
    HTTP_PUT,
80
 
    HTTP_DELETE
81
 
};
82
 
 
83
 
static const char *http_method_names[3] =
84
 
{
85
 
    "GET",
86
 
    "PUT",
87
 
    "DELETE"
88
 
};
89
 
 
90
 
enum http_state
91
 
{
92
 
    IDLE,
93
 
    CONNECTING,
94
 
    SENDING_REQUEST,
95
 
    SENDING_REQUEST_BODY,
96
 
    REQUEST_SENT,
97
 
    READING_RESPONSE,
98
 
    READING_DATA,
99
 
    READING_COMPLETE,
100
 
    ABORTING,
101
 
};
102
 
 
103
 
enum auth_state
104
 
{
105
 
    AUTH_NONE,          /* Not authenticating */
106
 
    AUTH_RETRYING,      /* New request with auth has been submitted */
107
 
    AUTH_DONE           /* Done retrying the request with auth. */
108
 
};
109
 
 
110
 
struct pj_http_req
111
 
{
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;
129
 
    struct tcp_state
130
 
    {
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)
133
 
         */
134
 
        pj_size_t tot_chunk_size;
135
 
        /* Size of data to be sent (in a single activesock operation).*/
136
 
        pj_size_t send_size;
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;
141
 
    } tcp_state;
142
 
};
143
 
 
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);
163
 
 
164
 
static pj_uint16_t get_http_default_port(const pj_str_t *protocol)
165
 
{
166
 
    int i;
167
 
 
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];
171
 
        }
172
 
    }
173
 
    return 0;
174
 
}
175
 
 
176
 
static const char * get_protocol(const pj_str_t *protocol)
177
 
{
178
 
    int i;
179
 
 
180
 
    for (i = 0; i < NUM_PROTOCOL; i++) {
181
 
        if (!pj_stricmp2(protocol, http_protocol_names[i])) {
182
 
            return http_protocol_names[i];
183
 
        }
184
 
    }
185
 
 
186
 
    /* Should not happen */
187
 
    pj_assert(0);
188
 
    return NULL;
189
 
}
190
 
 
191
 
 
192
 
/* Syntax error handler for parser. */
193
 
static void on_syntax_error(pj_scanner *scanner)
194
 
{
195
 
    PJ_UNUSED_ARG(scanner);
196
 
    PJ_THROW(PJ_EINVAL);  // syntax error
197
 
}
198
 
 
199
 
/* Callback when connection is established to the server */
200
 
static pj_bool_t http_on_connect(pj_activesock_t *asock,
201
 
                                 pj_status_t status)
202
 
{
203
 
    pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock);
204
 
 
205
 
    if (hreq->state == ABORTING || hreq->state == IDLE)
206
 
        return PJ_FALSE;
207
 
 
208
 
    if (status != PJ_SUCCESS) {
209
 
        hreq->error = status;
210
 
        pj_http_req_cancel(hreq, PJ_TRUE);
211
 
        return PJ_FALSE;
212
 
    }
213
 
 
214
 
    /* OK, we are connected. Start sending the request */
215
 
    hreq->state = SENDING_REQUEST;
216
 
    http_req_start_sending(hreq);
217
 
    return PJ_TRUE;
218
 
}
219
 
 
220
 
static pj_bool_t http_on_data_sent(pj_activesock_t *asock,
221
 
                                   pj_ioqueue_op_key_t *op_key,
222
 
                                   pj_ssize_t sent)
223
 
{
224
 
    pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock);
225
 
 
226
 
    PJ_UNUSED_ARG(op_key);
227
 
 
228
 
    if (hreq->state == ABORTING || hreq->state == IDLE)
229
 
        return PJ_FALSE;
230
 
 
231
 
    if (sent <= 0) {
232
 
        hreq->error = (sent < 0 ? -sent : PJLIB_UTIL_EHTTPLOST);
233
 
        pj_http_req_cancel(hreq, PJ_TRUE);
234
 
        return PJ_FALSE;
235
 
    }
236
 
 
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)
244
 
        {
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));
252
 
            } else {
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)
258
 
                {
259
 
                    /* Finish sending all the chunks, start reading
260
 
                     * the response.
261
 
                     */
262
 
                    hreq->state = REQUEST_SENT;
263
 
                    http_req_start_reading(hreq);
264
 
                    return PJ_TRUE;
265
 
                }
266
 
            }
267
 
            if (hreq->param.reqdata.total_size > 0 &&
268
 
                hreq->cb.on_send_data)
269
 
            {
270
 
                /* Call the callback for the application to provide
271
 
                 * the next chunk of data to be sent.
272
 
                 */
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.
277
 
                 */
278
 
                pj_assert(hreq->tcp_state.tot_chunk_size +
279
 
                          hreq->param.reqdata.size <=
280
 
                          hreq->param.reqdata.total_size);
281
 
            }
282
 
            http_req_start_sending(hreq);
283
 
        } else {
284
 
            /* No request body, proceed to reading the server's response. */
285
 
            hreq->state = REQUEST_SENT;
286
 
            http_req_start_reading(hreq);
287
 
        }
288
 
    }
289
 
    return PJ_TRUE;
290
 
}
291
 
 
292
 
static pj_bool_t http_on_data_read(pj_activesock_t *asock,
293
 
                                  void *data,
294
 
                                  pj_size_t size,
295
 
                                  pj_status_t status,
296
 
                                  pj_size_t *remainder)
297
 
{
298
 
    pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock);
299
 
 
300
 
    TRACE_((THIS_FILE, "\nData received: %d bytes", size));
301
 
 
302
 
    if (hreq->state == ABORTING || hreq->state == IDLE)
303
 
        return PJ_FALSE;
304
 
 
305
 
    if (hreq->state == READING_RESPONSE) {
306
 
        pj_status_t st;
307
 
        pj_size_t rem;
308
 
 
309
 
        if (status != PJ_SUCCESS && status != PJ_EPENDING) {
310
 
            hreq->error = status;
311
 
            pj_http_req_cancel(hreq, PJ_TRUE);
312
 
            return PJ_FALSE;
313
 
        }
314
 
 
315
 
        /* Parse the response. */
316
 
        st = http_response_parse(hreq->pool, &hreq->response,
317
 
                                 data, size, &rem);
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
321
 
             */
322
 
            if (size == BUF_SIZE) {
323
 
                hreq->error = PJ_ETOOBIG; // response header size is too big
324
 
                pj_http_req_cancel(hreq, PJ_TRUE);
325
 
                return PJ_FALSE;
326
 
            }
327
 
            /* Keep the data if we do not get the whole response header */
328
 
            *remainder = size;
329
 
        } else {
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
334
 
                 * to the user.
335
 
                 */
336
 
                hreq->response.data = data;
337
 
                hreq->response.size = size - rem;
338
 
            }
339
 
 
340
 
            /* If code is 401 or 407, find and parse WWW-Authenticate or
341
 
             * Proxy-Authenticate header
342
 
             */
343
 
            if (hreq->response.status_code == 401 ||
344
 
                hreq->response.status_code == 407)
345
 
            {
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;
350
 
                unsigned i;
351
 
 
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))
356
 
                    {
357
 
                        status = parse_auth_chal(hreq->pool,
358
 
                                                 &hdrs->header[i].value,
359
 
                                                 &response->auth_chal);
360
 
                        break;
361
 
                    }
362
 
                }
363
 
 
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))
375
 
                    )
376
 
                    {
377
 
                    /* Yes, authentication is required and we have been
378
 
                     * configured with credential.
379
 
                     */
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.
384
 
                         */
385
 
                        return PJ_FALSE;
386
 
                    }
387
 
                }
388
 
            }
389
 
 
390
 
            /* We already received the response header, call the
391
 
             * appropriate callback.
392
 
             */
393
 
            if (hreq->cb.on_response)
394
 
                (*hreq->cb.on_response)(hreq, &hreq->response);
395
 
            hreq->response.data = NULL;
396
 
            hreq->response.size = 0;
397
 
 
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);
402
 
        }
403
 
 
404
 
        return PJ_TRUE;
405
 
    }
406
 
 
407
 
    if (hreq->state != READING_DATA)
408
 
        return PJ_FALSE;
409
 
    if (hreq->cb.on_data_read) {
410
 
        /* If application wishes to receive the data once available, call
411
 
         * its callback.
412
 
         */
413
 
        if (size > 0)
414
 
            (*hreq->cb.on_data_read)(hreq, data, size);
415
 
    } else {
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.
420
 
             */
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);
426
 
        }
427
 
 
428
 
        /* If the size of data received exceeds its current size,
429
 
         * grow the buffer by a factor of 2.
430
 
         */
431
 
        if (hreq->tcp_state.current_read_size + size >
432
 
            hreq->response.size)
433
 
        {
434
 
            void *olddata = hreq->response.data;
435
 
 
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;
440
 
        }
441
 
 
442
 
        /* Append the response data. */
443
 
        pj_memcpy((char *)hreq->response.data +
444
 
                  hreq->tcp_state.current_read_size, data, size);
445
 
    }
446
 
    hreq->tcp_state.current_read_size += size;
447
 
 
448
 
    /* If the total data received so far is equal to the content length
449
 
     * or if it's already EOF.
450
 
     */
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))
455
 
    {
456
 
        /* Finish reading */
457
 
        http_req_end_request(hreq);
458
 
        hreq->response.size = hreq->tcp_state.current_read_size;
459
 
 
460
 
        /* HTTP request is completed, call the callback. */
461
 
        if (hreq->cb.on_complete) {
462
 
            (*hreq->cb.on_complete)(hreq, PJ_SUCCESS, &hreq->response);
463
 
        }
464
 
 
465
 
        return PJ_FALSE;
466
 
    }
467
 
 
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))
471
 
    {
472
 
        hreq->error = status;
473
 
        pj_http_req_cancel(hreq, PJ_TRUE);
474
 
        return PJ_FALSE;
475
 
    }
476
 
 
477
 
    return PJ_TRUE;
478
 
}
479
 
 
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)
483
 
{
484
 
    pj_http_req *hreq = (pj_http_req *) entry->user_data;
485
 
 
486
 
    PJ_UNUSED_ARG(timer_heap);
487
 
 
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).
491
 
     */
492
 
    if (hreq->state == READING_COMPLETE) {
493
 
        /* Yeah, we finish on time */
494
 
        return;
495
 
    }
496
 
 
497
 
    /* Invalidate id. */
498
 
    hreq->timer_entry.id = 0;
499
 
 
500
 
    /* Request timed out. */
501
 
    hreq->error = PJ_ETIMEDOUT;
502
 
    pj_http_req_cancel(hreq, PJ_TRUE);
503
 
}
504
 
 
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)
508
 
{
509
 
    pj_scanner scanner;
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;
517
 
    PJ_USE_EXCEPTION ;
518
 
 
519
 
    pj_scan_init(&scanner, input->ptr, input->slen, PJ_SCAN_AUTOSKIP_WS,
520
 
                 &on_syntax_error);
521
 
    PJ_TRY {
522
 
        /* Get auth scheme */
523
 
        if (*scanner.curptr == '"') {
524
 
            pj_scan_get_quote(&scanner, '"', '"', &chal->scheme);
525
 
            chal->scheme.ptr++;
526
 
            chal->scheme.slen -= 2;
527
 
        } else {
528
 
            pj_scan_get_until_chr(&scanner, " \t\r\n", &chal->scheme);
529
 
        }
530
 
 
531
 
        /* Loop parsing all parameters */
532
 
        for (;;) {
533
 
            const char *end_param = ", \t\r\n;";
534
 
            pj_str_t name, value;
535
 
 
536
 
            /* Get pair of parameter name and value */
537
 
            value.ptr = NULL;
538
 
            value.slen = 0;
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,
546
 
                                          &value);
547
 
                        value.ptr++;
548
 
                        value.slen -= 2;
549
 
                    } else if (!strchr(end_param, *scanner.curptr)) {
550
 
                        pj_scan_get_until_chr(&scanner, end_param, &value);
551
 
                    }
552
 
                }
553
 
                value = pj_str_unescape(pool, &value);
554
 
            }
555
 
 
556
 
            if (!pj_stricmp(&name, &REALM_STR)) {
557
 
                chal->realm = value;
558
 
 
559
 
            } else if (!pj_stricmp(&name, &NONCE_STR)) {
560
 
                chal->nonce = value;
561
 
 
562
 
            } else if (!pj_stricmp(&name, &ALGORITHM_STR)) {
563
 
                chal->algorithm = value;
564
 
 
565
 
            } else if (!pj_stricmp(&name, &OPAQUE_STR)) {
566
 
                chal->opaque = value;
567
 
 
568
 
            } else if (!pj_stricmp(&name, &QOP_STR)) {
569
 
                chal->qop = value;
570
 
 
571
 
            } else if (!pj_stricmp(&name, &STALE_STR)) {
572
 
                chal->stale = value.slen &&
573
 
                              (*value.ptr != '0') &&
574
 
                              (*value.ptr != 'f') &&
575
 
                              (*value.ptr != 'F');
576
 
 
577
 
            }
578
 
 
579
 
            /* Eat comma */
580
 
            if (!pj_scan_is_eof(&scanner) && *scanner.curptr == ',')
581
 
                pj_scan_get_char(&scanner);
582
 
            else
583
 
                break;
584
 
        }
585
 
 
586
 
    }
587
 
    PJ_CATCH_ANY {
588
 
        status = PJ_GET_EXCEPTION();
589
 
        pj_bzero(chal, sizeof(*chal));
590
 
        TRACE_((THIS_FILE, "Error: parsing of auth header failed"));
591
 
    }
592
 
    PJ_END;
593
 
    pj_scan_fini(&scanner);
594
 
    return status;
595
 
}
596
 
 
597
 
/* The same as #pj_http_headers_add_elmt() with char * as
598
 
 * its parameters.
599
 
 */
600
 
PJ_DEF(pj_status_t) pj_http_headers_add_elmt2(pj_http_headers *headers,
601
 
                                              char *name, char *val)
602
 
{
603
 
    pj_str_t f, v;
604
 
    pj_cstr(&f, name);
605
 
    pj_cstr(&v, val);
606
 
    return pj_http_headers_add_elmt(headers, &f, &v);
607
 
}
608
 
 
609
 
PJ_DEF(pj_status_t) pj_http_headers_add_elmt(pj_http_headers *headers,
610
 
                                             pj_str_t *name,
611
 
                                             pj_str_t *val)
612
 
{
613
 
    PJ_ASSERT_RETURN(headers && name && val, PJ_FALSE);
614
 
    if (headers->count >= PJ_HTTP_HEADER_SIZE)
615
 
        return PJ_ETOOMANY;
616
 
    pj_strassign(&headers->header[headers->count].name, name);
617
 
    pj_strassign(&headers->header[headers->count++].value, val);
618
 
    return PJ_SUCCESS;
619
 
}
620
 
 
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)
625
 
{
626
 
    pj_size_t i;
627
 
    char *cptr;
628
 
    char *end_status, *newdata;
629
 
    pj_scanner scanner;
630
 
    pj_str_t s;
631
 
    const pj_str_t STR_CONTENT_LENGTH = { CONTENT_LENGTH, 14 };
632
 
    pj_status_t status;
633
 
 
634
 
    PJ_USE_EXCEPTION;
635
 
 
636
 
    PJ_ASSERT_RETURN(response, PJ_EINVAL);
637
 
    if (size < 2)
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.
642
 
     */
643
 
    cptr = (char *)data;
644
 
    for (i = 1, cptr++; i < size; i++, cptr++) {
645
 
        if (*cptr == '\n') {
646
 
            if (*(cptr - 1) == '\n')
647
 
                break;
648
 
            if (*(cptr - 1) == '\r') {
649
 
                if (i >= 3 && *(cptr - 2) == '\n' && *(cptr - 3) == '\r')
650
 
                    break;
651
 
            }
652
 
        }
653
 
    }
654
 
    if (i == size)
655
 
        return PJLIB_UTIL_EHTTPINCHDR;
656
 
    *remainder = size - 1 - i;
657
 
 
658
 
    pj_bzero(response, sizeof(response));
659
 
    response->content_length = -1;
660
 
 
661
 
    newdata = (char*) pj_pool_alloc(pool, i);
662
 
    pj_memcpy(newdata, data, i);
663
 
 
664
 
    /* Parse the status-line. */
665
 
    pj_scan_init(&scanner, newdata, i, 0, &on_syntax_error);
666
 
    PJ_TRY {
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--;
675
 
    }
676
 
    PJ_CATCH_ANY {
677
 
        pj_scan_fini(&scanner);
678
 
        return PJ_GET_EXCEPTION();
679
 
    }
680
 
    PJ_END;
681
 
 
682
 
    end_status = scanner.curptr;
683
 
    pj_scan_fini(&scanner);
684
 
 
685
 
    /* Parse the response headers. */
686
 
    size = i - 2 - (end_status - newdata);
687
 
    if (size > 0) {
688
 
        status = http_headers_parse(end_status + 1, size,
689
 
                                    &response->headers);
690
 
    } else {
691
 
        status = PJ_SUCCESS;
692
 
    }
693
 
 
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))
698
 
        {
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.
703
 
             */
704
 
            if (response->content_length == 0) {
705
 
                if (pj_strcmp2(&response->headers.header[i].value, "0")) {
706
 
                    response->content_length = -1;
707
 
                }
708
 
            }
709
 
            break;
710
 
        }
711
 
    }
712
 
 
713
 
    return status;
714
 
}
715
 
 
716
 
static pj_status_t http_headers_parse(char *hdata, pj_size_t size,
717
 
                                      pj_http_headers *headers)
718
 
{
719
 
    pj_scanner scanner;
720
 
    pj_str_t s, s2;
721
 
    pj_status_t status;
722
 
    PJ_USE_EXCEPTION;
723
 
 
724
 
    PJ_ASSERT_RETURN(headers, PJ_EINVAL);
725
 
 
726
 
    pj_scan_init(&scanner, hdata, size, 0, &on_syntax_error);
727
 
 
728
 
    /* Parse each line of header field consisting of header field name and
729
 
     * value, separated by ":" and any number of white spaces.
730
 
     */
731
 
    PJ_TRY {
732
 
        do {
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')
738
 
                    s2.slen--;
739
 
                status = pj_http_headers_add_elmt(headers, &s, &s2);
740
 
                if (status != PJ_SUCCESS)
741
 
                    PJ_THROW(status);
742
 
            }
743
 
            pj_scan_advance_n(&scanner, 1, PJ_TRUE);
744
 
            /* Finish parsing */
745
 
            if (pj_scan_is_eof(&scanner))
746
 
                break;
747
 
        } while (1);
748
 
    }
749
 
    PJ_CATCH_ANY {
750
 
        pj_scan_fini(&scanner);
751
 
        return PJ_GET_EXCEPTION();
752
 
    }
753
 
    PJ_END;
754
 
 
755
 
    pj_scan_fini(&scanner);
756
 
 
757
 
    return PJ_SUCCESS;
758
 
}
759
 
 
760
 
PJ_DEF(void) pj_http_req_param_default(pj_http_req_param *param)
761
 
{
762
 
    pj_assert(param);
763
 
    pj_bzero(param, sizeof(*param));
764
 
    param->addr_family = pj_AF_INET();
765
 
    pj_strset2(&param->method, (char*)http_method_names[HTTP_GET]);
766
 
    pj_strset2(&param->version, (char*)HTTP_1_0);
767
 
    param->timeout.msec = PJ_HTTP_DEFAULT_TIMEOUT;
768
 
    pj_time_val_normalize(&param->timeout);
769
 
    param->max_retries = 3;
770
 
}
771
 
 
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.
775
 
 */
776
 
static char *get_url_at_pos(const char *str, long len)
777
 
{
778
 
    const char *end = str + len;
779
 
    const char *p = str;
780
 
 
781
 
    /* skip scheme: */
782
 
    while (p!=end && *p!='/') ++p;
783
 
    if (p!=end && *p=='/') ++p;
784
 
    if (p!=end && *p=='/') ++p;
785
 
    if (p==end) return NULL;
786
 
 
787
 
    for (; p!=end; ++p) {
788
 
        switch (*p) {
789
 
        case '/':
790
 
            return NULL;
791
 
        case '@':
792
 
            return (char*)p;
793
 
        }
794
 
    }
795
 
 
796
 
    return NULL;
797
 
}
798
 
 
799
 
 
800
 
PJ_DEF(pj_status_t) pj_http_req_parse_url(const pj_str_t *url,
801
 
                                          pj_http_url *hurl)
802
 
{
803
 
    pj_scanner scanner;
804
 
    int len = url->slen;
805
 
    PJ_USE_EXCEPTION;
806
 
 
807
 
    if (!len) return -1;
808
 
 
809
 
    pj_bzero(hurl, sizeof(*hurl));
810
 
    pj_scan_init(&scanner, url->ptr, url->slen, 0, &on_syntax_error);
811
 
 
812
 
    PJ_TRY {
813
 
        pj_str_t s;
814
 
 
815
 
        /* Exhaust any whitespaces. */
816
 
        pj_scan_skip_whitespace(&scanner);
817
 
 
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]);
826
 
        } else {
827
 
            PJ_THROW(PJ_ENOTSUP); // unsupported protocol
828
 
        }
829
 
 
830
 
        if (pj_scan_strcmp(&scanner, "://", 3)) {
831
 
            PJ_THROW(PJLIB_UTIL_EHTTPINURL); // no "://" after protocol name
832
 
        }
833
 
        pj_scan_advance_n(&scanner, 3, PJ_FALSE);
834
 
 
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);
841
 
            } else {
842
 
                hurl->passwd.slen = 0;
843
 
            }
844
 
            pj_scan_get_char(&scanner);
845
 
        }
846
 
 
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)
851
 
            PJ_THROW(PJ_EINVAL);
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);
857
 
        } else {
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);
862
 
            if (!hurl->port)
863
 
                PJ_THROW(PJLIB_UTIL_EHTTPINPORT); // invalid port number
864
 
        }
865
 
 
866
 
        if (!pj_scan_is_eof(&scanner)) {
867
 
            hurl->path.ptr = scanner.curptr;
868
 
            hurl->path.slen = scanner.end - scanner.curptr;
869
 
        } else {
870
 
            /* no path, append '/' */
871
 
            pj_cstr(&hurl->path, "/");
872
 
        }
873
 
    }
874
 
    PJ_CATCH_ANY {
875
 
        pj_scan_fini(&scanner);
876
 
        return PJ_GET_EXCEPTION();
877
 
    }
878
 
    PJ_END;
879
 
 
880
 
    pj_scan_fini(&scanner);
881
 
    return PJ_SUCCESS;
882
 
}
883
 
 
884
 
PJ_DEF(void) pj_http_req_set_timeout(pj_http_req *http_req,
885
 
                                     const pj_time_val* timeout)
886
 
{
887
 
    pj_memcpy(&http_req->param.timeout, timeout, sizeof(*timeout));
888
 
}
889
 
 
890
 
PJ_DEF(pj_status_t) pj_http_req_create(pj_pool_t *pool,
891
 
                                       const pj_str_t *url,
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)
897
 
{
898
 
    pj_pool_t *own_pool;
899
 
    pj_http_req *hreq;
900
 
    char *at_pos;
901
 
    pj_status_t status;
902
 
 
903
 
    PJ_ASSERT_RETURN(pool && url && timer && ioqueue &&
904
 
                     hcb && http_req, PJ_EINVAL);
905
 
 
906
 
    *http_req = NULL;
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);
910
 
    if (!hreq)
911
 
        return PJ_ENOMEM;
912
 
 
913
 
    /* Initialization */
914
 
    hreq->pool = own_pool;
915
 
    hreq->ioqueue = ioqueue;
916
 
    hreq->timer = timer;
917
 
    hreq->asock = NULL;
918
 
    pj_memcpy(&hreq->cb, hcb, sizeof(*hcb));
919
 
    hreq->state = IDLE;
920
 
    hreq->resolved = PJ_FALSE;
921
 
    hreq->buffer.ptr = NULL;
922
 
    pj_timer_entry_init(&hreq->timer_entry, 0, hreq, &on_timeout);
923
 
 
924
 
    /* Initialize parameter */
925
 
    if (param) {
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
932
 
         */
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),
938
 
                         PJ_ENOTSUP);
939
 
        pj_time_val_normalize(&hreq->param.timeout);
940
 
    } else {
941
 
        pj_http_req_param_default(&hreq->param);
942
 
    }
943
 
 
944
 
    /* Parse the URL */
945
 
    if (!pj_strdup_with_null(hreq->pool, &hreq->url, url)) {
946
 
        pj_pool_release(hreq->pool);
947
 
        return PJ_ENOMEM;
948
 
    }
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
953
 
    }
954
 
 
955
 
    /* If URL contains username/password, move them to credential and
956
 
     * remove them from the URL.
957
 
     */
958
 
    if ((at_pos=get_url_at_pos(hreq->url.ptr, hreq->url.slen)) != NULL) {
959
 
        pj_str_t tmp;
960
 
        char *user_pos = pj_strchr(&hreq->url, '/');
961
 
        int removed_len;
962
 
 
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);
966
 
 
967
 
        tmp = pj_str_unescape(hreq->pool, &hreq->hurl.passwd);
968
 
        pj_strdup(hreq->pool, &hreq->param.auth_cred.data, &tmp);
969
 
 
970
 
        hreq->hurl.username.ptr = hreq->hurl.passwd.ptr = NULL;
971
 
        hreq->hurl.username.slen = hreq->hurl.passwd.slen = 0;
972
 
 
973
 
        /* Remove "username:password@" from the URL */
974
 
        pj_assert(user_pos != 0 && user_pos < at_pos);
975
 
        user_pos += 2;
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;
979
 
 
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)
983
 
        {
984
 
            hreq->hurl.host.ptr -= removed_len;
985
 
        }
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)
989
 
        {
990
 
            hreq->hurl.path.ptr -= removed_len;
991
 
        }
992
 
    }
993
 
 
994
 
    *http_req = hreq;
995
 
    return PJ_SUCCESS;
996
 
}
997
 
 
998
 
PJ_DEF(pj_bool_t) pj_http_req_is_running(const pj_http_req *http_req)
999
 
{
1000
 
   PJ_ASSERT_RETURN(http_req, PJ_FALSE);
1001
 
   return (http_req->state != IDLE);
1002
 
}
1003
 
 
1004
 
PJ_DEF(void*) pj_http_req_get_user_data(pj_http_req *http_req)
1005
 
{
1006
 
    PJ_ASSERT_RETURN(http_req, NULL);
1007
 
    return http_req->param.user_data;
1008
 
}
1009
 
 
1010
 
static pj_status_t start_http_req(pj_http_req *http_req,
1011
 
                                  pj_bool_t notify_on_fail)
1012
 
{
1013
 
    pj_sock_t sock = PJ_INVALID_SOCKET;
1014
 
    pj_status_t status;
1015
 
    pj_activesock_cb asock_cb;
1016
 
    int retry = 0;
1017
 
 
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
1021
 
     */
1022
 
    PJ_ASSERT_RETURN(http_req->state == IDLE, PJ_EBUSY);
1023
 
 
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));
1028
 
 
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))
1038
 
        {
1039
 
            goto on_return;
1040
 
        }
1041
 
        http_req->resolved = PJ_TRUE;
1042
 
    }
1043
 
 
1044
 
    status = pj_sock_socket(http_req->param.addr_family, pj_SOCK_STREAM(),
1045
 
                            0, &sock);
1046
 
    if (status != PJ_SUCCESS)
1047
 
        goto on_return; // error creating socket
1048
 
 
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;
1053
 
 
1054
 
    do
1055
 
    {
1056
 
        pj_sockaddr_in bound_addr;
1057
 
        pj_uint16_t port = 0;
1058
 
 
1059
 
        /* If we are using port restriction.
1060
 
         * Get a random port within the range
1061
 
         */
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));
1066
 
        }
1067
 
 
1068
 
        pj_sockaddr_in_init(&bound_addr, NULL, port);
1069
 
        status = pj_sock_bind(sock, &bound_addr, sizeof(bound_addr));
1070
 
 
1071
 
    } while (status != PJ_SUCCESS && (retry++ < http_req->param.max_retries));
1072
 
 
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);
1077
 
        goto on_return;
1078
 
    }
1079
 
 
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
1088
 
    }
1089
 
 
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
1098
 
    }
1099
 
 
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)
1109
 
            goto on_return;
1110
 
    } else if (status != PJ_EPENDING) {
1111
 
        goto on_return; // error connecting
1112
 
    }
1113
 
 
1114
 
    return PJ_SUCCESS;
1115
 
 
1116
 
on_return:
1117
 
    http_req->error = status;
1118
 
    if (notify_on_fail)
1119
 
        pj_http_req_cancel(http_req, PJ_TRUE);
1120
 
    else
1121
 
        http_req_end_request(http_req);
1122
 
 
1123
 
    return status;
1124
 
}
1125
 
 
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)
1128
 
{
1129
 
    return start_http_req(http_req, PJ_FALSE);
1130
 
}
1131
 
 
1132
 
/* Respond to basic authentication challenge */
1133
 
static pj_status_t auth_respond_basic(pj_http_req *hreq)
1134
 
{
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
1140
 
     *
1141
 
     * Sample:
1142
 
     *       Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
1143
 
     */
1144
 
    pj_str_t user_pass;
1145
 
    pj_http_header_elmt *phdr;
1146
 
    int len;
1147
 
 
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);
1153
 
 
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");
1159
 
    else
1160
 
        phdr->name = pj_str("Proxy-Authorization");
1161
 
 
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;
1165
 
 
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;
1171
 
 
1172
 
    return PJ_SUCCESS;
1173
 
}
1174
 
 
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)
1179
 
 
1180
 
/* Transform digest to string.
1181
 
 * output must be at least PJSIP_MD5STRLEN+1 bytes.
1182
 
 *
1183
 
 * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
1184
 
 */
1185
 
static void digest2str(const unsigned char digest[], char *output)
1186
 
{
1187
 
    int i;
1188
 
    for (i = 0; i<16; ++i) {
1189
 
        pj_val_to_hex_digit(digest[i], output);
1190
 
        output += 2;
1191
 
    }
1192
 
}
1193
 
 
1194
 
static void auth_create_digest_response(pj_str_t *result,
1195
 
                                        const pj_http_auth_cred *cred,
1196
 
                                        const pj_str_t *nonce,
1197
 
                                        const pj_str_t *nc,
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)
1203
 
{
1204
 
    char ha1[MD5_STRLEN];
1205
 
    char ha2[MD5_STRLEN];
1206
 
    unsigned char digest[16];
1207
 
    pj_md5_context pms;
1208
 
 
1209
 
    pj_assert(result->slen >= MD5_STRLEN);
1210
 
 
1211
 
    TRACE_((THIS_FILE, "Begin creating digest"));
1212
 
 
1213
 
    if (cred->data_type == 0) {
1214
 
        /***
1215
 
         *** ha1 = MD5(username ":" realm ":" password)
1216
 
         ***/
1217
 
        pj_md5_init(&pms);
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);
1224
 
 
1225
 
        digest2str(digest, ha1);
1226
 
 
1227
 
    } else if (cred->data_type == 1) {
1228
 
        pj_assert(cred->data.slen == 32);
1229
 
        pj_memcpy( ha1, cred->data.ptr, cred->data.slen );
1230
 
    } else {
1231
 
        pj_assert(!"Invalid data_type");
1232
 
    }
1233
 
 
1234
 
    TRACE_((THIS_FILE, "  ha1=%.32s", ha1));
1235
 
 
1236
 
    /***
1237
 
     *** ha2 = MD5(method ":" req_uri)
1238
 
     ***/
1239
 
    pj_md5_init(&pms);
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);
1245
 
 
1246
 
    TRACE_((THIS_FILE, "  ha2=%.32s", ha2));
1247
 
 
1248
 
    /***
1249
 
     *** When qop is not used:
1250
 
     ***    response = MD5(ha1 ":" nonce ":" ha2)
1251
 
     ***
1252
 
     *** When qop=auth is used:
1253
 
     ***    response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
1254
 
     ***/
1255
 
    pj_md5_init(&pms);
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);
1266
 
    }
1267
 
    MD5_APPEND( &pms, ":", 1);
1268
 
    MD5_APPEND( &pms, ha2, MD5_STRLEN);
1269
 
 
1270
 
    /* This is the final response digest. */
1271
 
    pj_md5_final(&pms, digest);
1272
 
 
1273
 
    /* Convert digest to string and store in chal->response. */
1274
 
    result->slen = MD5_STRLEN;
1275
 
    digest2str(digest, result->ptr);
1276
 
 
1277
 
    TRACE_((THIS_FILE, "  digest=%.32s", result->ptr));
1278
 
    TRACE_((THIS_FILE, "Digest created"));
1279
 
}
1280
 
 
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)
1283
 
{
1284
 
    pj_str_t qop;
1285
 
    char *p;
1286
 
 
1287
 
    pj_strdup_with_null( pool, &qop, qop_offer);
1288
 
    p = qop.ptr;
1289
 
    while (*p) {
1290
 
        *p = (char)pj_tolower(*p);
1291
 
        ++p;
1292
 
    }
1293
 
 
1294
 
    p = qop.ptr;
1295
 
    while (*p) {
1296
 
        if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
1297
 
            int e = *(p+4);
1298
 
            if (e=='"' || e==',' || e==0)
1299
 
                return PJ_TRUE;
1300
 
            else
1301
 
                p += 4;
1302
 
        } else {
1303
 
            ++p;
1304
 
        }
1305
 
    }
1306
 
 
1307
 
    return PJ_FALSE;
1308
 
}
1309
 
 
1310
 
#define STR_PREC(s) (int)(s).slen, (s).ptr
1311
 
 
1312
 
/* Respond to digest authentication */
1313
 
static pj_status_t auth_respond_digest(pj_http_req *hreq)
1314
 
{
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];
1319
 
    int len;
1320
 
    pj_str_t digest_response;
1321
 
 
1322
 
    /* Check algorithm is supported. We only support MD5 */
1323
 
    if (chal->algorithm.slen!=0 &&
1324
 
        pj_stricmp2(&chal->algorithm, "MD5"))
1325
 
    {
1326
 
        TRACE_((THIS_FILE, "Error: Unsupported digest algorithm \"%.*s\"",
1327
 
                  chal->algorithm.slen, chal->algorithm.ptr));
1328
 
        return PJ_ENOTSUP;
1329
 
    }
1330
 
 
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");
1336
 
    else
1337
 
        phdr->name = pj_str("Proxy-Authorization");
1338
 
 
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= */
1347
 
          12 + /* qop=auth */
1348
 
          8 + /* nc=.. */
1349
 
          30 + /* cnonce= */
1350
 
          12 + chal->opaque.slen + /* opaque=".." */
1351
 
          0;
1352
 
    phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len);
1353
 
 
1354
 
    /* Configure buffer to temporarily store the digest */
1355
 
    digest_response.ptr = digest_response_buf;
1356
 
    digest_response.slen = MD5_STRLEN;
1357
 
 
1358
 
    if (chal->qop.slen == 0) {
1359
 
        const pj_str_t STR_MD5 = { "MD5", 3 };
1360
 
 
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);
1366
 
 
1367
 
        len = pj_ansi_snprintf(
1368
 
                phdr->value.ptr, len,
1369
 
                "Digest username=\"%.*s\", "
1370
 
                "realm=\"%.*s\", "
1371
 
                "nonce=\"%.*s\", "
1372
 
                "uri=\"%.*s\", "
1373
 
                "algorithm=%.*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),
1379
 
                STR_PREC(STR_MD5),
1380
 
                STR_PREC(digest_response));
1381
 
        if (len < 0)
1382
 
            return PJ_ETOOSMALL;
1383
 
        phdr->value.slen = len;
1384
 
 
1385
 
    } else if (auth_has_qop(hreq->pool, &chal->qop)) {
1386
 
        /* Server requires quality of protection.
1387
 
         * We respond with selecting "qop=auth" protection.
1388
 
         */
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");
1393
 
 
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\", "
1401
 
                "realm=\"%.*s\", "
1402
 
                "nonce=\"%.*s\", "
1403
 
                "uri=\"%.*s\", "
1404
 
                "algorithm=%.*s, "
1405
 
                "response=\"%.*s\", "
1406
 
                "qop=%.*s, "
1407
 
                "nc=%.*s, "
1408
 
                "cnonce=\"%.*s\"",
1409
 
                STR_PREC(cred->username),
1410
 
                STR_PREC(chal->realm),
1411
 
                STR_PREC(chal->nonce),
1412
 
                STR_PREC(hreq->hurl.path),
1413
 
                STR_PREC(STR_MD5),
1414
 
                STR_PREC(digest_response),
1415
 
                STR_PREC(qop),
1416
 
                STR_PREC(nc),
1417
 
                STR_PREC(cnonce));
1418
 
        if (len < 0)
1419
 
            return PJ_ETOOSMALL;
1420
 
        phdr->value.slen = len;
1421
 
 
1422
 
        if (chal->opaque.slen) {
1423
 
            pj_strcat2(&phdr->value, ", opaque=\"");
1424
 
            pj_strcat(&phdr->value, &chal->opaque);
1425
 
            pj_strcat2(&phdr->value, "\"");
1426
 
        }
1427
 
 
1428
 
    } else {
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));
1432
 
        return PJ_ENOTSUP;
1433
 
    }
1434
 
 
1435
 
    return PJ_SUCCESS;
1436
 
}
1437
 
 
1438
 
 
1439
 
static void restart_req_with_auth(pj_http_req *hreq)
1440
 
{
1441
 
    pj_http_auth_chal *chal = &hreq->response.auth_chal;
1442
 
    pj_http_auth_cred *cred = &hreq->param.auth_cred;
1443
 
    pj_status_t status;
1444
 
 
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;
1448
 
        return;
1449
 
    }
1450
 
 
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"));
1455
 
        goto on_error;
1456
 
    }
1457
 
 
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"));
1462
 
        goto on_error;
1463
 
    }
1464
 
 
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);
1469
 
    } else {
1470
 
        TRACE_((THIS_FILE, "Error: unsupported HTTP auth scheme"));
1471
 
        status = PJ_ENOTSUP;
1472
 
    }
1473
 
 
1474
 
    if (status != PJ_SUCCESS)
1475
 
        goto on_error;
1476
 
 
1477
 
    http_req_end_request(hreq);
1478
 
 
1479
 
    status = start_http_req(hreq, PJ_TRUE);
1480
 
    if (status != PJ_SUCCESS)
1481
 
        goto on_error;
1482
 
 
1483
 
    hreq->auth_state = AUTH_RETRYING;
1484
 
    return;
1485
 
 
1486
 
on_error:
1487
 
    hreq->auth_state = AUTH_DONE;
1488
 
}
1489
 
 
1490
 
 
1491
 
/* snprintf() to a pj_str_t struct with an option to append the
1492
 
 * result at the back of the string.
1493
 
 */
1494
 
void str_snprintf(pj_str_t *s, size_t size,
1495
 
                  pj_bool_t append, const char *format, ...)
1496
 
{
1497
 
    va_list arg;
1498
 
    int retval;
1499
 
 
1500
 
    va_start(arg, format);
1501
 
    if (!append)
1502
 
        s->slen = 0;
1503
 
    size -= s->slen;
1504
 
    retval = pj_ansi_vsnprintf(s->ptr + s->slen,
1505
 
                               size, format, arg);
1506
 
    s->slen += ((retval < (int)size) ? retval : size - 1);
1507
 
    va_end(arg);
1508
 
}
1509
 
 
1510
 
static pj_status_t http_req_start_sending(pj_http_req *hreq)
1511
 
{
1512
 
    pj_status_t status;
1513
 
    pj_str_t pkt;
1514
 
    pj_ssize_t len;
1515
 
    pj_size_t i;
1516
 
 
1517
 
    PJ_ASSERT_RETURN(hreq->state == SENDING_REQUEST ||
1518
 
                     hreq->state == SENDING_REQUEST_BODY, PJ_EBUG);
1519
 
 
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);
1525
 
        pkt.slen = 0;
1526
 
        /* Start-line */
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])) {
1536
 
            char buf[16];
1537
 
 
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);
1544
 
        }
1545
 
 
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));
1551
 
        }
1552
 
        if (pkt.slen >= BUF_SIZE - 1) {
1553
 
            status = PJLIB_UTIL_EHTTPINSBUF;
1554
 
            goto on_return;
1555
 
        }
1556
 
 
1557
 
        pj_strcat2(&pkt, "\r\n");
1558
 
        pkt.ptr[pkt.slen] = 0;
1559
 
        TRACE_((THIS_FILE, "%s", pkt.ptr));
1560
 
    } else {
1561
 
        pkt.ptr = (char*)hreq->param.reqdata.data;
1562
 
        pkt.slen = hreq->param.reqdata.size;
1563
 
    }
1564
 
 
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,
1571
 
                                pkt.ptr, &len, 0);
1572
 
 
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
1577
 
    }
1578
 
 
1579
 
    return PJ_SUCCESS;
1580
 
 
1581
 
on_return:
1582
 
    http_req_end_request(hreq);
1583
 
    return status;
1584
 
}
1585
 
 
1586
 
static pj_status_t http_req_start_reading(pj_http_req *hreq)
1587
 
{
1588
 
    pj_status_t status;
1589
 
 
1590
 
    PJ_ASSERT_RETURN(hreq->state == REQUEST_SENT, PJ_EBUG);
1591
 
 
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) {
1599
 
        /* Error reading */
1600
 
        http_req_end_request(hreq);
1601
 
        return status;
1602
 
    }
1603
 
 
1604
 
    return PJ_SUCCESS;
1605
 
}
1606
 
 
1607
 
static pj_status_t http_req_end_request(pj_http_req *hreq)
1608
 
{
1609
 
    if (hreq->asock) {
1610
 
        pj_activesock_close(hreq->asock);
1611
 
        hreq->asock = NULL;
1612
 
    }
1613
 
 
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;
1619
 
    }
1620
 
 
1621
 
    hreq->state = IDLE;
1622
 
 
1623
 
    return PJ_SUCCESS;
1624
 
}
1625
 
 
1626
 
PJ_DEF(pj_status_t) pj_http_req_cancel(pj_http_req *http_req,
1627
 
                                       pj_bool_t notify)
1628
 
{
1629
 
    http_req->state = ABORTING;
1630
 
 
1631
 
    http_req_end_request(http_req);
1632
 
 
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);
1636
 
    }
1637
 
 
1638
 
    return PJ_SUCCESS;
1639
 
}
1640
 
 
1641
 
 
1642
 
PJ_DEF(pj_status_t) pj_http_req_destroy(pj_http_req *http_req)
1643
 
{
1644
 
    PJ_ASSERT_RETURN(http_req, PJ_EINVAL);
1645
 
 
1646
 
    /* If there is any pending request, cancel it */
1647
 
    if (http_req->state != IDLE) {
1648
 
        pj_http_req_cancel(http_req, PJ_FALSE);
1649
 
    }
1650
 
 
1651
 
    pj_pool_release(http_req->pool);
1652
 
 
1653
 
    return PJ_SUCCESS;
1654
 
}