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

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/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
}