1/*
2 * libjingle
3 * Copyright 2004--2010, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/base/asynchttprequest.h"
29
30namespace talk_base {
31
32enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE };
33static const int kDefaultHTTPTimeout = 30 * 1000;  // 30 sec
34
35///////////////////////////////////////////////////////////////////////////////
36// AsyncHttpRequest
37///////////////////////////////////////////////////////////////////////////////
38
39AsyncHttpRequest::AsyncHttpRequest(const std::string &user_agent)
40    : firewall_(NULL), port_(80), secure_(false),
41      timeout_(kDefaultHTTPTimeout), fail_redirect_(false),
42      factory_(Thread::Current()->socketserver(), user_agent),
43      pool_(&factory_), client_(user_agent.c_str(), &pool_), error_(HE_NONE)  {
44  client_.SignalHttpClientComplete.connect(this,
45      &AsyncHttpRequest::OnComplete);
46}
47
48AsyncHttpRequest::~AsyncHttpRequest() {
49}
50
51void AsyncHttpRequest::OnWorkStart() {
52  factory_.SetProxy(proxy_);
53  if (secure_)
54    factory_.UseSSL(host_.c_str());
55
56  bool transparent_proxy = (port_ == 80) &&
57           ((proxy_.type == PROXY_HTTPS) || (proxy_.type == PROXY_UNKNOWN));
58  if (transparent_proxy) {
59    client_.set_proxy(proxy_);
60  }
61  client_.set_fail_redirect(fail_redirect_);
62  client_.set_server(SocketAddress(host_, port_));
63
64  LOG(LS_INFO) << "HttpRequest start: " << host_ + client_.request().path;
65
66  Thread::Current()->PostDelayed(timeout_, this, MSG_TIMEOUT);
67  client_.start();
68}
69
70void AsyncHttpRequest::OnWorkStop() {
71  // worker is already quitting, no need to explicitly quit
72  LOG(LS_INFO) << "HttpRequest cancelled";
73}
74
75void AsyncHttpRequest::OnComplete(HttpClient* client, HttpErrorType error) {
76  Thread::Current()->Clear(this, MSG_TIMEOUT);
77
78  set_error(error);
79  if (!error) {
80    LOG(LS_INFO) << "HttpRequest completed successfully";
81
82    std::string value;
83    if (client_.response().hasHeader(HH_LOCATION, &value)) {
84      response_redirect_ = value.c_str();
85    }
86  } else {
87    LOG(LS_INFO) << "HttpRequest completed with error: " << error;
88  }
89
90  worker()->Quit();
91}
92
93void AsyncHttpRequest::OnMessage(Message* message) {
94  if (message->message_id != MSG_TIMEOUT) {
95    SignalThread::OnMessage(message);
96    return;
97  }
98
99  LOG(LS_INFO) << "HttpRequest timed out";
100  client_.reset();
101  worker()->Quit();
102}
103
104void AsyncHttpRequest::DoWork() {
105  // Do nothing while we wait for the request to finish. We only do this so
106  // that we can be a SignalThread; in the future this class should not be
107  // a SignalThread, since it does not need to spawn a new thread.
108  Thread::Current()->ProcessMessages(kForever);
109}
110
111}  // namespace talk_base
112