diff --git a/module/VuFindHttp/.gitignore b/module/VuFindHttp/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6b567c718f3d9032cbd8f8c0729e257faffa3913
--- /dev/null
+++ b/module/VuFindHttp/.gitignore
@@ -0,0 +1,7 @@
+TAGS
+\#*
+.\#*
+ChangeLog
+review
+test.php
+vendor/
diff --git a/module/VuFindHttp/LICENSE b/module/VuFindHttp/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..4eb3f1e9596a3213c1eeba7ec401ecac70ff1801
--- /dev/null
+++ b/module/VuFindHttp/LICENSE
@@ -0,0 +1,278 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/module/VuFindHttp/Module.php b/module/VuFindHttp/Module.php
new file mode 100644
index 0000000000000000000000000000000000000000..472f3c95c77d2db8f8075e57039800d34e98fd78
--- /dev/null
+++ b/module/VuFindHttp/Module.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * ZF2 module definition for the VF2 proxy service
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Proxy
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+namespace VuFindHttp;
+
+/**
+ * ZF2 module definition for the VF2 HTTP service.
+ *
+ * @category Proxy
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+class Module
+{
+
+    /**
+     * Relative path to search service configuration.
+     *
+     * @var string
+     */
+    protected $configPath = 'config/http.conf.php';
+
+    /**
+     * Return autoloader configuration.
+     *
+     * @return array
+     */
+    public function getAutoloaderConfig ()
+    {
+        return array(
+            'Zend\Loader\StandardAutoloader' => array(
+                'namespaces' => array(
+                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
+                ),
+            ),
+        );
+    }
+
+    /**
+     * Return module configuration.
+     *
+     * @return array
+     */
+    public function getConfig ()
+    {
+        return array();
+    }
+
+    /**
+     * Initialize module.
+     *
+     * @return void
+     */
+    public function init ()
+    {
+    }
+
+    /**
+     * Return service configuration.
+     *
+     * @return array
+     */
+    public function getServiceConfig ()
+    {
+        return array(
+            'factories' => array(
+                'VuFind\Http' => array($this, 'setup'),
+            )
+        );
+    }
+
+    /**
+     * Return configured http service to superior service manager.
+     *
+     * @return \VuFind\Service\Search
+     */
+    public function setup ()
+    {
+        $config = include realpath(__DIR__ . '/' . $this->configPath);
+
+        $di = new \Zend\Di\Di();
+        $di->configure(new \Zend\Di\Config($config));
+        return $di->get('VuFindHttp\HttpService');
+    }
+
+}
\ No newline at end of file
diff --git a/module/VuFindHttp/README.org b/module/VuFindHttp/README.org
new file mode 100644
index 0000000000000000000000000000000000000000..d824c0051ddcaa1eb2bc8f85578d98784656f4f9
--- /dev/null
+++ b/module/VuFindHttp/README.org
@@ -0,0 +1,114 @@
+* Proxy service API
+
+The terms MUST NOT, SHOULD NOT, SHOULD, MUST, and MAY are used as defined by [RFC2119].
+
+The proxy service provides access to HTTP-based internal and external resources. The services can be
+configured to proxy requests to external resources identified by the host part of the http URI.
+
+The following hosts are considered as as internal resources:
+
+  - localhost (IPv4 local host)
+  - 127/8 (IPv4 loopback network addresses)
+  - [::1] (IPv6 loopback address)
+
+Other VF2 components SHOULD use the proxy service in order to integrate in environments that require
+proxy access.
+
+* Public API
+
+The proxy service implements the following public methods:
+
+** get(string URL[, array PARAMS[, float TIMEOUT]])
+
+Performs a GET request to URL using optional PARAMS and TIMEOUT, and returns the response.
+
+PARAMS is an array of request parameters. It can either be an associative array of parameter names
+and associated values or a numeric array with properly URL-encoded name-value pairs in the form
+`NAME=VALUE'
+
+In the first case the query string is created with PHP's built-in http_build_query() function
+([BUILDQUERY]), in the latter case the name-value pairs are simply concatenated to form the query
+string.
+
+TIMEOUT is a float with the request timeout in seconds.
+
+** postForm(string URL[, array PARAMS[, float TIMEOUT]])
+
+Performs a POST request of regular form data to URL using optional PARAMS and optional
+HEADERS. PARAMS is converted to a query string using the same rules as in get() and send inside a
+POST request body with a content type of `application/x-www-form-urlencoded'.
+
+TIMEOUT is a float with the request timeout in seconds.
+
+** post(string URL[, mixed BODY[, string TYPE[, float TIMEOUT]]])
+
+Performs a POST request of optional BODY of TYPE to URL and returns the repose.
+
+BODY is casted to string.
+
+A component using the service SHOULD provide an appropriate Content-Type header for the POST request
+body in accordance with [MIMETYPES]. The default MIME type is `application/octet-stream'. The proxy
+service calculates the Content-Length request header value by performing `strlen()' on the request
+body.
+
+TIMEOUT is a float with the request timeout in seconds.
+
+** proxify(Zend\Http\Client CLIENT[, array OPTIONS])
+
+Add proxy settings to CLIENT with optional OPTIONS.
+
+CLIENT is an existing Zend HTTP client. The proxify() method replaces the HTTP connection adapter by
+a Zend Framework HTTP proxy adapter. OPTIONS is an array of Zend HTTP proxy options that should be
+used in addition to the service's proxy configuration. The proxy service proxy configuration is
+superior to the ones supplied via OPTIONS.
+
+** createClient(string URL[, string METHOD, [float TIMEOUT]])
+
+Return new Zend HTTP client to access URI by optional METHOD with optional TIMEOUT. CLIENT is
+proxified using the service's proxify() function.
+
+* Error handling
+
+The proxy service uses a marker interface [MARKER INTERFACE] to mark its exception. The proxy
+service catches all exceptions from the Zend HTTP client and wraps them in either a proxy service
+specific RuntimeException or InvalidArgumentException. The proxy service does not catch or emit any
+other exceptions.
+
+* Configuration
+
+The proxy service is configured by an PHP array in `conf/proxy.conf.php'. This array MUST contain
+the key `proxy' which points to an associative array with proxy configuration and MAY contain a key
+`adapter' with the name or an instance of a Zend HTTP client adapter class. 
+
+The `proxy' configuration array has the same semantics as the Zend HTTP proxy adapter
+configuration[ZEND ADAPTER]. No proxying is done if the proxy configuration array is empty.
+
+The class denoted in the `adapter' key is used as the HTTP connection adapter for get(), post(), and
+postForm().
+
+* Extension points
+
+The proxy service does not provide any extensions points.
+
+* Service identifier
+
+The proxy service registers under the name `VuFind\Http' in the web application's top level
+service manager.
+
+* Implementation
+
+A first implementation of this specification can be found at [VF2 PROXY].
+
+* References
+
+[BUILDQUERY] : http://php.net/manual/de/function.http-build-query.php
+
+[MARKER INTERFACE] : Marker interface pattern, http://en.wikipedia.org/wiki/Marker_interface_pattern
+
+[MIMETYPES] : MIME Media Types, http://www.iana.org/assignments/media-types/index.html
+
+[RFC2119] : Key words for use in RFCs to Indicate Requirement Levels, http://www.ietf.org/rfc/rfc2119.txt
+
+[VF2 PROXY] : https://github.com/dmj/vf2-proxy
+
+[ZEND ADAPTER] : Zend_Http_Client - Connection Adapters, http://packages.zendframework.com/docs/latest/manual/en/modules/zend.http.client.adapters.html
diff --git a/module/VuFindHttp/config/http.conf.php b/module/VuFindHttp/config/http.conf.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e539c30e99d6e77873fbfcacdb11e3b2ae5856c
--- /dev/null
+++ b/module/VuFindHttp/config/http.conf.php
@@ -0,0 +1,11 @@
+<?php
+
+return array(
+    'instance' => array(
+        'VuFindHttp\HttpService' => array(
+            'parameters' => array(
+                'proxyConfig' => array(),
+            )
+        )
+    )
+);
diff --git a/module/VuFindHttp/src/VuFindHttp/Exception/ExceptionInterface.php b/module/VuFindHttp/src/VuFindHttp/Exception/ExceptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d87354613d7e07624dfbe9e465f1ebedd6fb28f
--- /dev/null
+++ b/module/VuFindHttp/src/VuFindHttp/Exception/ExceptionInterface.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Proxy system exception marker interface.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Http
+ * @package  Exception
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+namespace VuFindHttp\Exception;
+
+/**
+ * Proxy system exception marker interface.
+ *
+ * @category Http
+ * @package  Exception
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+interface ExceptionInterface
+{
+}
\ No newline at end of file
diff --git a/module/VuFindHttp/src/VuFindHttp/Exception/RuntimeException.php b/module/VuFindHttp/src/VuFindHttp/Exception/RuntimeException.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d51397908bf2702335e9669c76bb3585802251a
--- /dev/null
+++ b/module/VuFindHttp/src/VuFindHttp/Exception/RuntimeException.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * Proxy system RuntimeException.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Http
+ * @package  Exception
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+namespace VuFindHttp\Exception;
+
+/**
+ * Proxy system RuntimeException.
+ *
+ * This exception is thrown if the request failed on the network level
+ * (e.g. timeout).
+ *
+ * @category Http
+ * @package  Exception
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
\ No newline at end of file
diff --git a/module/VuFindHttp/src/VuFindHttp/HttpService.php b/module/VuFindHttp/src/VuFindHttp/HttpService.php
new file mode 100644
index 0000000000000000000000000000000000000000..728c5dbbca729c26659cc7c571c3531781c6a142
--- /dev/null
+++ b/module/VuFindHttp/src/VuFindHttp/HttpService.php
@@ -0,0 +1,244 @@
+<?php
+
+/**
+ * VuFind HTTP service class file.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Http
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-HTTP
+ */
+
+namespace VuFindHttp;
+
+/**
+ * VuFind HTTP service.
+ *
+ * @category Http
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-search-subsystem
+ */
+class HttpService implements HttpServiceInterface
+{
+
+    /**
+     * Regular expression matching a request to localhost.
+     *
+     * @var string
+     */
+    const LOCAL_ADDRESS_RE = '@^(localhost|127(\.\d+){3}|\[::1\])@';
+
+    /**
+     * Proxy configuration.
+     *
+     * @see \Zend\Http\Client\Adapter\Proxy::$config
+     *
+     * @var array
+     */
+    protected $proxyConfig;
+
+    /**
+     * Constructor.
+     *
+     * @param array $proxyConfig Proxy configuration
+     *
+     * @return void
+     */
+    public function __construct (array $proxyConfig = array())
+    {
+        $this->proxyConfig = $proxyConfig;
+    }
+
+    /**
+     * Proxify an existing client.
+     *
+     * Returns the client given as argument with appropriate proxy setup.
+     *
+     * @param Zend\Http\Client $client  HTTP client
+     * @param array            $options ZF2 ProxyAdapter options
+     *
+     * @return Zend\Http\Client
+     */
+    public function proxify (\Zend\Http\Client $client, array $options = array())
+    {
+        if ($this->proxyConfig) {
+            $host = $client->getUri()->getHost();
+            if (!$this->isLocal($host)) {
+                $adapter = new \Zend\Http\Client\Adapter\Proxy();
+                $options = array_replace($this->proxyConfig, $options);
+                $adapter->setOptions($options);
+                $client->setAdapter($adapter);
+            }
+        }
+        return $client;
+    }
+
+    /**
+     * Perform a GET request.
+     *
+     * @param string $url     Request URL
+     * @param array  $params  Request parameters
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Response
+     */
+    public function get ($url, array $params = array(), $timeout = null)
+    {
+        if ($params) {
+            $query = $this->createQueryString($params);
+            if (strpos($url, '?') !== false) {
+                $url .= '&' . $query;
+            } else {
+                $url .= '?' . $query;
+            }
+        }
+        $client = $this->createClient($url, \Zend\Http\Request::METHOD_GET, $timeout);
+        return $this->send($client);
+    }
+
+    /**
+     * Perform a POST request.
+     *
+     * @param string $url     Request URL
+     * @param mixed  $body    Request body document
+     * @param string $type    Request body content type
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Response
+     */
+    public function post ($url, $body = null, $type = 'application/octet-stream', $timeout = null)
+    {
+        $client = $this->createClient($url, \Zend\Http\Request::METHOD_POST, $timeout);
+        $client->setRawBody($body);
+        $client->setHeaders(array('Content-Type' => $type, 'Content-Length' => strlen($body)));
+        return $this->send($client);
+    }
+
+    /**
+     * Post form data.
+     *
+     * @param string $url     Request URL
+     * @param array  $params  Form data
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Response
+     */
+    public function postForm ($url, array $params = array(), $timeout = null)
+    {
+        $body = $this->createQueryString($params);
+        return $this->post($url, $body, \Zend\Http\Client::ENC_URLENCODED, $timeout);
+    }
+
+    /**
+     * Return a new HTTP client.
+     *
+     * @param string $url     Target URL
+     * @param string $method  Request method
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Client
+     */
+    public function createClient ($url, $method = \Zend\Http\Request::METHOD_GET, $timeout = null)
+    {
+        $client = new \Zend\Http\Client();
+        $client->setMethod($method);
+        $client->setUri($url);
+        if ($timeout) {
+            $client->setOptions(array('timeout' => $timeout));
+        }
+        $this->proxify($client);
+        return $client;
+    }
+
+    /// Internal API
+
+    /**
+     * Return query string based on params.
+     *
+     * @param array $params Parameters
+     *
+     * @return string
+     */
+    protected function createQueryString (array $params = array())
+    {
+        if ($this->isAssocParams($params)) {
+            return http_build_query($params);
+        } else {
+            return implode('&', $params);
+        }
+    }
+
+    /**
+     * Send HTTP request and return response.
+     *
+     * @param \Zend\Http\Client $client HTTP client to use
+     *
+     * @throws Exception\RuntimeException
+     * @return \Zend\Http\Response
+     *
+     * @todo Catch more exceptions, maybe?
+     */
+    protected function send (\Zend\Http\Client $client)
+    {
+        try {
+            $response = $client->send();
+        } catch (\Zend\Http\Client\Exception\RuntimeException $e) {
+            throw new Exception\RuntimeException(
+                sprintf('Zend HTTP Client exception: %s', $e),
+                -1,
+                $e
+            );
+        }
+        return $response;
+    }
+
+    /**
+     * Return TRUE if argument is an associative array.
+     *
+     * @param array $array Array to test
+     *
+     * @return boolean
+     */
+    public static function isAssocParams (array $array)
+    {
+        foreach ($array as $key => $value) {
+            if (!is_numeric($key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return TRUE if argument refers to localhost.
+     *
+     * @param string $host Host to check
+     *
+     * @return boolean
+     */
+    protected function isLocal ($host)
+    {
+        return preg_match(self::LOCAL_ADDRESS_RE, $host);
+    }
+
+}
\ No newline at end of file
diff --git a/module/VuFindHttp/src/VuFindHttp/HttpServiceAwareInterface.php b/module/VuFindHttp/src/VuFindHttp/HttpServiceAwareInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..16153250787d9f2caa42e4c886644e2bf8a89747
--- /dev/null
+++ b/module/VuFindHttp/src/VuFindHttp/HttpServiceAwareInterface.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Marker interface for HTTP service aware classes.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Http
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+namespace VuFindHttp;
+
+/**
+ * Marker interface for HTTP service aware classes.
+ *
+ * @category Http
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+interface HttpServiceAwareInterface
+{
+    /**
+     * Set the HTTP service to be used for HTTP requests.
+     *
+     * @param HttpServiceInterface $service HTTP service
+     *
+     * @return void
+     */
+    public function setHttpService (HttpServiceInterface $service);
+}
\ No newline at end of file
diff --git a/module/VuFindHttp/src/VuFindHttp/HttpServiceInterface.php b/module/VuFindHttp/src/VuFindHttp/HttpServiceInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..456f59f938b39bb9267d9d5fd2a481f100783ae0
--- /dev/null
+++ b/module/VuFindHttp/src/VuFindHttp/HttpServiceInterface.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * VuFind HTTP service interface definition.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Http
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+namespace VuFindHttp;
+
+/**
+ * VuFind HTTP service interface definition.
+ *
+ * @category Http
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+interface HttpServiceInterface
+{
+    /**
+     * Proxify an existing client.
+     *
+     * Returns the client given as argument with appropriate proxy setup.
+     *
+     * @param Zend\Http\Client $client  HTTP client
+     * @param array            $options ZF2 ProxyAdapter options
+     *
+     * @return Zend\Http\Client
+     */
+    public function proxify (\Zend\Http\Client $client, array $options = array());
+
+    /**
+     * Perform a GET request.
+     *
+     * @param string $url     Request URL
+     * @param array  $params  Request parameters
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Response
+     */
+    public function get ($url, array $params = array(), $timeout = null);
+
+    /**
+     * Perform a POST request.
+     *
+     * @param string $url     Request URL
+     * @param mixed  $body    Request body document
+     * @param string $type    Request body content type
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Response
+     */
+    public function post ($url, $body = null, $type = 'application/octet-stream', $timeout = null);
+
+    /**
+     * Post form data.
+     *
+     * @param string $url     Request URL
+     * @param array  $params  Form data
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Response
+     */
+    public function postForm ($url, array $params = array(), $timeout = null);
+
+    /**
+     * Return a new proxy client.
+     *
+     * @param string $url     Target URL
+     * @param string $method  Request method
+     * @param float  $timeout Request timeout in seconds
+     *
+     * @return \Zend\Http\Client
+     */
+    public function createClient ($url, $method = \Zend\Http\Request::METHOD_GET, $timeout = null);
+
+}
\ No newline at end of file
diff --git a/module/VuFindHttp/tests/unit-tests/bootstrap.php b/module/VuFindHttp/tests/unit-tests/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..fcc2fd66998d8ca46f44ac734d8ee9bd9fbb76b9
--- /dev/null
+++ b/module/VuFindHttp/tests/unit-tests/bootstrap.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * Proxy service PHPUnit bootstrap.
+ *
+ * @author    David Maus <maus@hab.de>
+ * @license   http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @copyright Copyright (C) Villanova University 2011
+ *
+ */
+
+require_once('Zend/Loader/AutoloaderFactory.php');
+\Zend\Loader\AutoloaderFactory::factory(
+    array(
+        'Zend\Loader\StandardAutoloader' => array(
+            'namespaces' => array(
+                'VuFindHttp' => realpath(__DIR__ . '/../../src/VuFindHttp'),
+                'VuFindTest' => realpath(__DIR__ . '/src/VuFindTest'),
+            ),
+            'autoregister_zf' => true
+        )
+    )
+);
diff --git a/module/VuFindHttp/tests/unit-tests/phpunit.xml b/module/VuFindHttp/tests/unit-tests/phpunit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1cbaaf01a44dd4b15defbdbccc0ca8f4795c4722
--- /dev/null
+++ b/module/VuFindHttp/tests/unit-tests/phpunit.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<phpunit bootstrap="bootstrap.php">
+  <testsuites>
+    <testsuite name="core-proxy">
+      <directory>src</directory>
+    </testsuite>
+  </testsuites>
+  <logging>
+    <log type="coverage-html" target="../../review/coverage"/>
+  </logging>
+  <php>
+    <includePath>../../vendor/zf2/library</includePath>
+  </php>
+</phpunit>
diff --git a/module/VuFindHttp/tests/unit-tests/src/VuFindTest/HttpServiceTest.php b/module/VuFindHttp/tests/unit-tests/src/VuFindTest/HttpServiceTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6072628503b50584da24887843f6398f1b1d8998
--- /dev/null
+++ b/module/VuFindHttp/tests/unit-tests/src/VuFindTest/HttpServiceTest.php
@@ -0,0 +1,238 @@
+<?php
+
+/**
+ * Proxy service unit test.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Search
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+namespace VuFindTest;
+
+use VuFindHttp\HttpService as Service;
+
+/**
+ * Proxy service unit test.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * @category Search
+ * @package  Service
+ * @author   David Maus <maus@hab.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://github.com/dmj/vf2-proxy
+ */
+
+class ProxyServiceTest extends \PHPUnit_Framework_TestCase
+{
+
+    protected $local = array('ipv4 localhost' => 'http://localhost',
+                             'ipv4 loopback'  => 'http://127.0.0.1',
+                             'ipv6 loopback'  => 'http://[::1]');
+
+    /**
+     * Test GET request with associative array.
+     *
+     * @return void
+     */
+    public function testGetWithAssocParams ()
+    {
+        $service = new Service();
+        $adapter = $this->getMock('Zend\Http\Client\Adapter\Test', array('write'));
+        $adapter->expects($this->once())
+            ->method('write')
+            ->with(
+                $this->equalTo('GET'),
+                $this->equalTo(
+                    new \Zend\Uri\Http('http://example.tld?foo=bar&bar%5B0%5D=baz')
+                )
+            );
+        $service->setDefaultAdapter($adapter);
+        $service->get('http://example.tld', array('foo' => 'bar', 'bar' => array('baz')));
+    }
+
+    /**
+     * Test GET request with parameter pairs.
+     *
+     * @return void
+     */
+    public function testGetWithParamPairs ()
+    {
+        $service = new Service();
+        $adapter = $this->getMock('Zend\Http\Client\Adapter\Test', array('write'));
+        $adapter->expects($this->once())
+            ->method('write')
+            ->with(
+                $this->equalTo('GET'),
+                $this->equalTo(
+                    new \Zend\Uri\Http('http://example.tld?foo=bar&bar=baz&bar=bar')
+                )
+            );
+        $service->setDefaultAdapter($adapter);
+        $service->get('http://example.tld', array('foo=bar', 'bar=baz', 'bar=bar'));
+    }
+
+    /**
+     * Test GET request appends query part.
+     *
+     * @return void
+     */
+    public function testGetAppendsQueryPart ()
+    {
+        $service = new Service();
+        $adapter = $this->getMock('Zend\Http\Client\Adapter\Test', array('write'));
+        $adapter->expects($this->once())
+            ->method('write')
+            ->with(
+                $this->equalTo('GET'),
+                $this->equalTo(
+                    new \Zend\Uri\Http('http://example.tld?foo=bar&bar=baz')
+                )
+            );
+        $service->setDefaultAdapter($adapter);
+        $service->get('http://example.tld?foo=bar', array('bar=baz'));
+    }
+
+    /**
+     * Test POST request of form data.
+     *
+     * @return void
+     */
+    public function testPostForm ()
+    {
+        $service = new Service();
+        $adapter = new \Zend\Http\Client\Adapter\Test();
+        $service->setDefaultAdapter($adapter);
+        $service->postForm('http://example.tld', array('foo=bar'));
+    }
+
+    /**
+     * Test POST request.with empty body
+     *
+     * @return void
+     */
+    public function testSendPostRequestEmptyBody ()
+    {
+        $service = new Service();
+        $adapter = $this->getMock('Zend\Http\Client\Adapter\Test', array('write'));
+        $adapter->expects($this->once())
+            ->method('write')
+            ->with(
+                $this->equalTo('POST'),
+                $this->equalTo(
+                    new \Zend\Uri\Http('http://example.tld')
+                )
+            );
+        $service->setDefaultAdapter($adapter);
+        $service->post('http://example.tld');
+    }
+
+    /**
+     * Test proxify.
+     *
+     * @return void
+     */
+    public function testProxify ()
+    {
+        $service = new Service(
+            array(
+                'proxy_host' => 'localhost',
+                'proxy_port' => '666'
+            )
+        );
+        $client = new \Zend\Http\Client('http://example.tld:8080');
+        $client = $service->proxify($client);
+        $adapter = $client->getAdapter();
+        $this->assertInstanceOf('Zend\Http\Client\Adapter\Proxy', $adapter);
+        $config = $adapter->getConfig();
+        $this->assertEquals('localhost', $config['proxy_host']);
+        $this->assertEquals('666', $config['proxy_port']);
+    }
+
+    /**
+     * Test no proxify with local address.
+     *
+     * @return void
+     */
+    public function testNoProxifyLocal ()
+    {
+        $service = new Service(
+            array(
+                'proxy_host' => 'localhost',
+                'proxy_port' => '666'
+            )
+        );
+        foreach ($this->local as $name => $address) {
+            $client = new \Zend\Http\Client($address);
+            $client->setAdapter(new \Zend\Http\Client\Adapter\Test());
+            $client = $service->proxify($client);
+            $this->assertInstanceOf(
+                'Zend\Http\Client\Adapter\Test',
+                $client->getAdapter(),
+                sprintf('Failed to proxify %s: %s', $name, $address)
+            );
+        }
+    }
+
+    /**
+     * Test for runtime exception.
+     *
+     * @expectedException \VuFindHttp\Exception\RuntimeException
+     *
+     * @return void
+     */
+    public function testRuntimeException ()
+    {
+        $service = new Service();
+        $service->get('http://example.tld');
+    }
+
+    /**
+     * Test isAssocArray with mixed keys.
+     *
+     * @return void
+     */
+    public function testIsAssocArrayWithMixedKeys ()
+    {
+        $arr = array();
+        $arr[] = 'foo';
+        $arr['foo'] = 'bar';
+        $this->assertTrue(Service::isAssocParams($arr));
+    }
+
+}
\ No newline at end of file