1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "HTTPStream"
19#include <utils/Log.h>
20
21#include "include/HTTPStream.h"
22
23#include <sys/socket.h>
24
25#include <arpa/inet.h>
26#include <ctype.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <netdb.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include <media/stagefright/foundation/ADebug.h>
36
37namespace android {
38
39// static
40const char *HTTPStream::kStatusKey = ":status:";
41
42HTTPStream::HTTPStream()
43    : mState(READY),
44      mSocket(-1) {
45}
46
47HTTPStream::~HTTPStream() {
48    disconnect();
49}
50
51static bool MakeSocketBlocking(int s, bool blocking) {
52    // Make socket non-blocking.
53    int flags = fcntl(s, F_GETFL, 0);
54    if (flags == -1) {
55        return false;
56    }
57
58    if (blocking) {
59        flags &= ~O_NONBLOCK;
60    } else {
61        flags |= O_NONBLOCK;
62    }
63
64    return fcntl(s, F_SETFL, flags) != -1;
65}
66
67static status_t MyConnect(
68        int s, const struct sockaddr *addr, socklen_t addrlen) {
69    status_t result = UNKNOWN_ERROR;
70
71    MakeSocketBlocking(s, false);
72
73    if (connect(s, addr, addrlen) == 0) {
74        result = OK;
75    } else if (errno != EINPROGRESS) {
76        result = -errno;
77    } else {
78        for (;;) {
79            fd_set rs, ws;
80            FD_ZERO(&rs);
81            FD_ZERO(&ws);
82            FD_SET(s, &rs);
83            FD_SET(s, &ws);
84
85            struct timeval tv;
86            tv.tv_sec = 0;
87            tv.tv_usec = 100000ll;
88
89            int nfds = ::select(s + 1, &rs, &ws, NULL, &tv);
90
91            if (nfds < 0) {
92                if (errno == EINTR) {
93                    continue;
94                }
95
96                result = -errno;
97                break;
98            }
99
100            if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) {
101                result = OK;
102                break;
103            }
104
105            if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
106                // Get the pending error.
107                int error = 0;
108                socklen_t errorLen = sizeof(error);
109                if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) {
110                    // Couldn't get the real error, so report why not.
111                    result = -errno;
112                } else {
113                    result = -error;
114                }
115                break;
116            }
117
118            // Timeout expired. Try again.
119        }
120    }
121
122    MakeSocketBlocking(s, true);
123
124    return result;
125}
126
127// Apparently under our linux closing a socket descriptor from one thread
128// will not unblock a pending send/recv on that socket on another thread.
129static ssize_t MySendReceive(
130        int s, void *data, size_t size, int flags, bool sendData) {
131    ssize_t result = 0;
132
133    if (s < 0) {
134        return -1;
135    }
136    while (size > 0) {
137        fd_set rs, ws, es;
138        FD_ZERO(&rs);
139        FD_ZERO(&ws);
140        FD_ZERO(&es);
141        FD_SET(s, sendData ? &ws : &rs);
142        FD_SET(s, &es);
143
144        struct timeval tv;
145        tv.tv_sec = 0;
146        tv.tv_usec = 100000ll;
147
148        int nfds = ::select(
149                s + 1,
150                sendData ? NULL : &rs,
151                sendData ? &ws : NULL,
152                &es,
153                &tv);
154
155        if (nfds < 0) {
156            if (errno == EINTR) {
157                continue;
158            }
159
160            result = -errno;
161            break;
162        } else if (nfds == 0) {
163            // timeout
164
165            continue;
166        }
167
168        CHECK_EQ(nfds, 1);
169
170        ssize_t nbytes =
171            sendData ? send(s, data, size, flags) : recv(s, data, size, flags);
172
173        if (nbytes < 0) {
174            if (errno == EINTR) {
175                continue;
176            }
177
178            result = -errno;
179            break;
180        } else if (nbytes == 0) {
181            result = 0;
182            break;
183        }
184
185        data = (uint8_t *)data + nbytes;
186        size -= nbytes;
187
188        result = nbytes;
189        break;
190    }
191
192    return result;
193}
194
195static ssize_t MySend(int s, const void *data, size_t size, int flags) {
196    return MySendReceive(
197            s, const_cast<void *>(data), size, flags, true /* sendData */);
198}
199
200static ssize_t MyReceive(int s, void *data, size_t size, int flags) {
201    return MySendReceive(s, data, size, flags, false /* sendData */);
202}
203
204status_t HTTPStream::connect(const char *server, int port) {
205    Mutex::Autolock autoLock(mLock);
206
207    status_t err = OK;
208
209    if (mState == CONNECTED) {
210        return ERROR_ALREADY_CONNECTED;
211    }
212
213    if (port < 0 || port > (int) USHRT_MAX) {
214        return UNKNOWN_ERROR;
215    }
216
217    char service[sizeof("65536")];
218    sprintf(service, "%d", port);
219    struct addrinfo hints, *ai;
220    memset(&hints, 0, sizeof(hints));
221    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
222    hints.ai_socktype = SOCK_STREAM;
223
224    int ret = getaddrinfo(server, service, &hints, &ai);
225    if (ret) {
226        return ERROR_UNKNOWN_HOST;
227    }
228
229    CHECK_EQ(mSocket, -1);
230
231    mState = CONNECTING;
232    status_t res = -1;
233    struct addrinfo *tmp;
234    for (tmp = ai; tmp; tmp = tmp->ai_next) {
235        mSocket = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
236        if (mSocket < 0) {
237            continue;
238        }
239
240        setReceiveTimeout(30);  // Time out reads after 30 secs by default.
241
242        int s = mSocket;
243
244        mLock.unlock();
245
246        res = MyConnect(s, tmp->ai_addr, tmp->ai_addrlen);
247
248        mLock.lock();
249
250        if (mState != CONNECTING) {
251            close(s);
252            freeaddrinfo(ai);
253            return UNKNOWN_ERROR;
254        }
255
256        if (res == OK) {
257            break;
258        }
259
260        close(s);
261    }
262
263    freeaddrinfo(ai);
264
265    if (res != OK) {
266        close(mSocket);
267        mSocket = -1;
268
269        mState = READY;
270        return res;
271    }
272
273    mState = CONNECTED;
274
275    return OK;
276}
277
278status_t HTTPStream::disconnect() {
279    Mutex::Autolock autoLock(mLock);
280
281    if (mState != CONNECTED && mState != CONNECTING) {
282        return ERROR_NOT_CONNECTED;
283    }
284
285    CHECK(mSocket >= 0);
286    close(mSocket);
287    mSocket = -1;
288
289    mState = READY;
290
291    return OK;
292}
293
294status_t HTTPStream::send(const char *data, size_t size) {
295    if (mState != CONNECTED) {
296        return ERROR_NOT_CONNECTED;
297    }
298
299    while (size > 0) {
300        ssize_t n = MySend(mSocket, data, size, 0);
301
302        if (n < 0) {
303            disconnect();
304
305            return n;
306        } else if (n == 0) {
307            disconnect();
308
309            return ERROR_CONNECTION_LOST;
310        }
311
312        size -= (size_t)n;
313        data += (size_t)n;
314    }
315
316    return OK;
317}
318
319status_t HTTPStream::send(const char *data) {
320    return send(data, strlen(data));
321}
322
323// A certain application spawns a local webserver that sends invalid responses,
324// specifically it terminates header line with only a newline instead of the
325// CRLF (carriage-return followed by newline) required by the HTTP specs.
326// The workaround accepts both behaviours but could potentially break
327// legitimate responses that use a single newline to "fold" headers, which is
328// why it's not yet on by default.
329#define WORKAROUND_FOR_MISSING_CR       1
330
331status_t HTTPStream::receive_line(char *line, size_t size) {
332    if (mState != CONNECTED) {
333        return ERROR_NOT_CONNECTED;
334    }
335
336    bool saw_CR = false;
337    size_t length = 0;
338
339    for (;;) {
340        char c;
341        ssize_t n = MyReceive(mSocket, &c, 1, 0);
342        if (n < 0) {
343            disconnect();
344
345            return ERROR_IO;
346        } else if (n == 0) {
347            disconnect();
348
349            return ERROR_CONNECTION_LOST;
350        }
351
352#if WORKAROUND_FOR_MISSING_CR
353        if (c == '\n') {
354            // We have a complete line.
355
356            line[saw_CR ? length - 1 : length] = '\0';
357            return OK;
358        }
359#else
360        if (saw_CR &&  c == '\n') {
361            // We have a complete line.
362
363            line[length - 1] = '\0';
364            return OK;
365        }
366#endif
367
368        saw_CR = (c == '\r');
369
370        if (length + 1 >= size) {
371            return ERROR_MALFORMED;
372        }
373        line[length++] = c;
374    }
375}
376
377status_t HTTPStream::receive_header(int *http_status) {
378    *http_status = -1;
379    mHeaders.clear();
380
381    char line[2048];
382    status_t err = receive_line(line, sizeof(line));
383    if (err != OK) {
384        return err;
385    }
386
387    mHeaders.add(string(kStatusKey), string(line));
388
389    char *spacePos = strchr(line, ' ');
390    if (spacePos == NULL) {
391        // Malformed response?
392        return UNKNOWN_ERROR;
393    }
394
395    char *status_start = spacePos + 1;
396    char *status_end = status_start;
397    while (isdigit(*status_end)) {
398        ++status_end;
399    }
400
401    if (status_end == status_start) {
402        // Malformed response, status missing?
403        return UNKNOWN_ERROR;
404    }
405
406    memmove(line, status_start, status_end - status_start);
407    line[status_end - status_start] = '\0';
408
409    long tmp = strtol(line, NULL, 10);
410    if (tmp < 0 || tmp > 999) {
411        return UNKNOWN_ERROR;
412    }
413
414    *http_status = (int)tmp;
415
416    for (;;) {
417        err = receive_line(line, sizeof(line));
418        if (err != OK) {
419            return err;
420        }
421
422        if (*line == '\0') {
423            // Empty line signals the end of the header.
424            break;
425        }
426
427        // puts(line);
428
429        char *colonPos = strchr(line, ':');
430        if (colonPos == NULL) {
431            mHeaders.add(string(line), string());
432        } else {
433            char *end_of_key = colonPos;
434            while (end_of_key > line && isspace(end_of_key[-1])) {
435                --end_of_key;
436            }
437
438            char *start_of_value = colonPos + 1;
439            while (isspace(*start_of_value)) {
440                ++start_of_value;
441            }
442
443            *end_of_key = '\0';
444
445            mHeaders.add(string(line), string(start_of_value));
446        }
447    }
448
449    return OK;
450}
451
452ssize_t HTTPStream::receive(void *data, size_t size) {
453    size_t total = 0;
454    while (total < size) {
455        ssize_t n = MyReceive(mSocket, (char *)data + total, size - total, 0);
456
457        if (n < 0) {
458            LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n));
459
460            disconnect();
461            return (ssize_t)ERROR_IO;
462        } else if (n == 0) {
463            disconnect();
464
465            LOGE("recv failed, server is gone, total received: %d bytes",
466                 total);
467
468            return total == 0 ? (ssize_t)ERROR_CONNECTION_LOST : total;
469        }
470
471        total += (size_t)n;
472    }
473
474    return (ssize_t)total;
475}
476
477bool HTTPStream::find_header_value(const string &key, string *value) const {
478    ssize_t index = mHeaders.indexOfKey(key);
479    if (index < 0) {
480        value->clear();
481        return false;
482    }
483
484    *value = mHeaders.valueAt(index);
485
486    return true;
487}
488
489void HTTPStream::setReceiveTimeout(int seconds) {
490    if (seconds < 0) {
491        // Disable the timeout.
492        seconds = 0;
493    }
494
495    struct timeval tv;
496    tv.tv_usec = 0;
497    tv.tv_sec = seconds;
498    CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)));
499}
500
501}  // namespace android
502
503