/* * Baltisot * Copyright (C) 1999-2007 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: HttpClient.cc,v 1.10 2007-05-30 11:57:09 pixel Exp $ */ #include #include #include #include #include t_headers no_headers; const timeval timeout = { 8, 0 }; HttpClient::HttpClient(const String & _url, Handle * _out, const String & _fake_host, t_headers _headers) : url(_url), out(_out), fake_host(_fake_host), headers(_headers), host(""), uri("") { DecodeURL(); Client.SetNonBlock(); Client.Connect(fake_host == "" ? host : fake_host, 80); if (Client.IsConnected()) { SetBurst(); } else { WaitFor(timeout); WaitFor(&Client, W4_WRITING); } } HttpClient::~HttpClient() { } int HttpClient::Do() throw (GeneralException) { t_headers::iterator i; String t; int l; Regex h_reply("^HTTP/1.1 "); Regex chunked_header("^Transfer-Encoding: chunked$"); switch (current) { case 0: if (Client.IsConnecting() && (TaskMan::Event() == Task::EVT_HANDLE)) { Client.FinalizeConnect(); } if (TaskMan::Event() == Task::EVT_TIMEOUT) { status = "Connection timeout."; return TASK_DONE; } if (!Client.IsConnected()) { return TASK_DONE; } RemoveTimeout(); b << "GET " + uri + " HTTP/1.1\r\n" "Host: " + host + "\r\n" "Connection: close\r\n"; for (i = headers.begin(); i != headers.end(); i++) { b << *i + "\r\n"; } b << "\r\n"; c = new CopyJob(&b, &Client); WaitFor(timeout); WaitFor(c); current = 1; Suspend(TASK_ON_HOLD); case 1: delete c; if (TaskMan::Event() == Task::EVT_TIMEOUT) { status = "Connection timeout."; return TASK_DONE; } RemoveTimeout(); c = new ReadJob(&Client, &b); WaitFor(timeout); WaitFor(c); current = 2; Suspend(TASK_ON_HOLD); case 2: delete c; if (TaskMan::Event() == Task::EVT_TIMEOUT) { status = "Connection timeout."; return TASK_DONE; } RemoveTimeout(); b >> t; if (!h_reply.Match(t)) { http_code = 0; status = "Invalid answer from HTTP server."; return TASK_DONE; } reply = t.extract(9); chunked = false; while (t.strlen()) { b >> t; reply_headers.push_back(t); if (chunked_header.Match(t)) { chunked = true; } } http_code = reply.to_int(); if (http_code != 200) { status = "Reply code != 200."; return TASK_DONE; } status = "Downloading."; if (!chunked) { current = 5; c = new CopyJob(&Client, out); WaitFor(timeout); WaitFor(c); Suspend(TASK_ON_HOLD); } c = 0; case 3: if (c) delete c; if (TaskMan::Event() == Task::EVT_TIMEOUT) { status = "Connection timeout."; return TASK_DONE; } RemoveTimeout(); c = new ReadJob(&Client, &b, any); WaitFor(timeout); WaitFor(c); current = 4; Suspend(TASK_ON_HOLD); case 4: delete c; if (TaskMan::Event() == Task::EVT_TIMEOUT) { status = "Connection timeout."; return TASK_DONE; } RemoveTimeout(); b >> t; if (t.strlen() == 0) { current = 3; c = 0; Suspend(TASK_BURST); } l = t.to_int("%x"); if (l == 0) { status = "Downloaded."; return TASK_DONE; } c = new CopyJob(&Client, out, l); WaitFor(timeout); WaitFor(c); current = 3; Suspend(TASK_ON_HOLD); case 5: delete c; status = "Downloaded."; return TASK_DONE; } return TASK_ON_HOLD; } String HttpClient::GetStatus() { return status; } void HttpClient::DecodeURL() throw (GeneralException) { int p; static const Regex isURLValid("^http://[^/]"); if (!isURLValid.Match(url)) throw GeneralException("Invalid URL."); String tmp = url.extract(7); p = tmp.strchr('/'); if (p < 0) { host = tmp; return; } host = tmp.extract(0, p - 1); uri = tmp.extract(p); }