/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Nam Nguyen
*/
package com.caucho.quercus.lib.curl;
import com.caucho.quercus.QuercusModuleException;
import com.caucho.quercus.env.BooleanValue;
import com.caucho.quercus.env.Env;
import com.caucho.quercus.env.EnvCleanup;
import com.caucho.quercus.env.StringValue;
import com.caucho.quercus.env.Value;
import com.caucho.util.L10N;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
/**
* Represents a generic Http request.
*/
public class HttpRequest
implements EnvCleanup {
private static final Logger log = Logger.getLogger(HttpRequest.class.getName());
private static final L10N L = new L10N(HttpRequest.class);
private CurlResource _curl;
private HttpConnection _conn;
public HttpRequest(CurlResource curlResource) {
_curl = curlResource;
}
/**
* Returns a HttpRequest specific to the Http request method.
*/
public static HttpRequest getRequest(CurlResource curl) {
String requestMethod = curl.getRequestMethod();
if (requestMethod.equals("GET")) {
return new HttpGetRequest(curl);
} else if (requestMethod.equals("POST")) {
return new HttpPostRequest(curl);
} else if (requestMethod.equals("PUT")) {
return new HttpPutRequest(curl);
} else {
return new HttpRequest(curl);
}
}
/**
* Opens the connection.
*/
protected final void create(Env env)
throws MalformedURLException, IOException {
URL url = getURL(env, _curl.getURL(), _curl.getPort());
if (url == null) {
return;
}
if (_curl.getIsProxying()) {
URL proxyURL = getURL(env, _curl.getProxyURL(), _curl.getProxyPort());
_conn = HttpConnection.createConnection(url,
_curl.getUsername(),
_curl.getPassword(),
_curl,
proxyURL,
_curl.getProxyUsername(),
_curl.getProxyPassword(),
_curl.getProxyType());
} else {
_conn = HttpConnection.createConnection(url,
_curl.getUsername(),
_curl.getPassword(),
_curl);
}
}
/**
* Initializes the connection.
*/
protected boolean init(Env env)
throws ProtocolException {
if (_conn == null || _curl == null) {
return false;
}
_conn.setRequestMethod(_curl.getRequestMethod());
HashMap<String, String> _properties = _curl.getRequestPropertiesMap();
for (Map.Entry<String, String> entry : _properties.entrySet()) {
_conn.setRequestProperty(entry.getKey(), entry.getValue());
}
_conn.setInstanceFollowRedirects(_curl.getIsFollowingRedirects());
int timeout = _curl.getConnectTimeout();
if (timeout >= 0) {
_conn.setConnectTimeout(timeout);
}
timeout = _curl.getReadTimeout();
if (timeout >= 0) {
_conn.setReadTimeout(timeout);
}
return true;
}
/**
* Attempt to connect to the server.
*/
protected void connect(Env env)
throws ConnectException, SocketTimeoutException,
UnknownHostException, IOException {
if (_conn != null) {
_conn.connect(_curl);
}
}
/**
* Transfer data to the server.
*/
protected void transfer(Env env)
throws IOException {
}
/**
* Closes the connection and sends data and connection info to curl.
*/
protected boolean finish(Env env)
throws IOException {
if (_curl == null || _conn == null) {
return false;
}
_curl.setResponseCode(_conn.getResponseCode());
Value header = getHeader(env, env.createBinaryBuilder());
if (header == BooleanValue.FALSE) {
return false;
}
_curl.setHeader(header.toStringValue());
Value body = getBody(env, env.createBinaryBuilder());
if (body == BooleanValue.FALSE) {
return false;
}
_curl.setBody(body.toStringValue());
_curl.setContentLength(_conn.getContentLength());
_curl.setCookie(_conn.getHeaderField("Set-Cookie"));
_conn.close();
return true;
}
/**
* Perform this request.
*/
public final boolean execute(Env env) {
try {
create(env);
if (!init(env)) {
return false;
}
connect(env);
transfer(env);
return finish(env);
} catch (MalformedURLException e) {
error(env, CurlModule.CURLE_URL_MALFORMAT, e.getMessage(), e);
return false;
} catch (SocketTimeoutException e) {
error(
env, CurlModule.CURLE_OPERATION_TIMEOUTED, "connection timed out", e);
return false;
} catch (ConnectException e) {
error(env, CurlModule.CURLE_COULDNT_CONNECT, e.getMessage(), e);
return false;
} catch (ProtocolException e) {
throw new QuercusModuleException(e.getMessage());
//error(0, e.getMessage(), e);
} catch (UnknownHostException e) {
error(env, CurlModule.CURLE_COULDNT_RESOLVE_HOST,
"unknown host: " + e.getMessage(), e);
return false;
} catch (IOException e) {
error(env, CurlModule.CURLE_RECV_ERROR, e.getMessage(), e);
return false;
}
}
protected final CurlResource getCurlResource() {
return _curl;
}
protected final HttpConnection getHttpConnection() {
return _conn;
}
protected final void error(Env env, int code, String error) {
log.log(Level.FINE, error);
if (_curl.getIsVerbose()) {
env.warning(L.l(error));
}
_curl.setError(error);
_curl.setErrorCode(code);
}
protected final void error(Env env, int code, String error, Throwable e) {
log.log(Level.FINE, error, e);
if (_curl.getIsVerbose()) {
env.warning(L.l(error));
}
_curl.setError(error);
_curl.setErrorCode(code);
}
/**
* Returns a valid URL or null on error.
*/
protected final URL getURL(Env env, String urlString, int port)
throws MalformedURLException {
if (urlString == null) {
return null;
}
URL url;
if (urlString.indexOf("://") < 0) {
url = new URL("http://" + urlString);
} else {
url = new URL(urlString);
}
if (port >= 0) {
url = new URL(url.getProtocol(), url.getHost(), port, url.getFile());
}
return url;
}
/**
* Returns the server response header.
*/
private Value getHeader(Env env, StringValue bb) {
// Append server response to the very top
bb.append(_conn.getHeaderField(0));
bb.append("\r\n");
if (_curl.getHeaderCallback() != null) {
StringValue sb = env.createUnicodeBuilder();
sb.append(_conn.getHeaderField(0));
sb.append("\r\n");
Value len = _curl.getHeaderCallback().call(env, env.wrapJava(_curl), sb);
if (len.toInt() != sb.length()) {
_curl.setErrorCode(CurlModule.CURLE_WRITE_ERROR);
return BooleanValue.FALSE;
}
}
String key;
int i = 1;
while ((key = _conn.getHeaderFieldKey(i)) != null) {
bb.append(key);
bb.append(": ");
bb.append(_conn.getHeaderField(i));
bb.append("\r\n");
if (_curl.getHeaderCallback() != null) {
StringValue sb = env.createUnicodeBuilder();
sb.append(key);
sb.append(": ");
sb.append(_conn.getHeaderField(i));
sb.append("\r\n");
Value len = _curl.getHeaderCallback().call(env,
env.wrapJava(_curl),
sb);
if (len.toInt() != sb.length()) {
_curl.setErrorCode(CurlModule.CURLE_WRITE_ERROR);
return BooleanValue.FALSE;
}
}
i++;
}
bb.append("\r\n");
if (_curl.getHeaderCallback() != null) {
StringValue sb = env.createUnicodeBuilder();
sb.append("\r\n");
Value len = _curl.getHeaderCallback().call(env,
env.wrapJava(_curl),
sb);
if (len.toInt() != sb.length()) {
_curl.setErrorCode(CurlModule.CURLE_WRITE_ERROR);
return BooleanValue.FALSE;
}
}
return bb;
}
/**
* Returns the server response body.
*/
private Value getBody(Env env, StringValue bb)
throws SocketTimeoutException, IOException {
InputStream in;
if ((_conn.getResponseCode() < 400)) {
in = _conn.getInputStream();
} else {
in = _conn.getErrorStream();
}
if (in == null) {
return StringValue.EMPTY;
}
String encoding = _conn.getHeaderField("Content-Encoding");
if (encoding != null) {
if (encoding.equals("gzip")) {
in = new GZIPInputStream(in);
} else if (encoding.equals("deflate")) {
in = new InflaterInputStream(in);
} else if (encoding.equals("identity")) {
} else {
_curl.setError(encoding);
_curl.setErrorCode(CurlModule.CURLE_BAD_CONTENT_ENCODING);
return StringValue.EMPTY;
}
}
int ch;
try {
while ((ch = in.read()) >= 0) {
bb.appendByte(ch);
}
} catch (IOException e) {
throw new QuercusModuleException(e);
}
if (_curl.getReadCallback() != null) {
Value len = _curl.getReadCallback().call(env, env.wrapJava(_curl), bb);
if (len.toInt() != bb.length()) {
_curl.setErrorCode(CurlModule.CURLE_WRITE_ERROR);
return BooleanValue.FALSE;
}
}
return bb;
}
/**
* Cleanup resources associated with this connection.
*/
@Override
public void cleanup() {
if (_conn != null) {
_conn.close();
}
}
}