Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

We have a bespoke Wordpress plugin, which adds a meta box to the edit post page. The plugin accesses data from a semi-private HTTP REST API.

The connection is authenticated (DIGEST) and accessed regularly - every person writing a post will interact with it several times (usually in tight bursts) and there are > 100 such people. So, we would like to ensure the HTTP calls are working efficiently. We see two problems:

  • No evidence of Keep-Alive being used to stop the TCP socket opening and closing constantly.
  • Every request is done twice for the DIGEST authentication. Since it is the plugin that is authenticated (not the user) this is > 99% waste. Apache have called their solution for this pre-emptive authentication.

We can see latency in the meta box as users interact with it, so this is a real issue.

I hear some of these problems can be solved by re-using the cURL handle, but cannot find any best practice advice around implementing that in a WordPress plugin specifically. One issue we have with that is that WordPRess effectively re-initialises the plugin on a per request basis, as far as we can tell.

share|improve this question
    
What is "bespoke". –  Wyck Jan 16 '13 at 14:02
    
bespoke means made for us. i.e. not available in the marketplace, not open source etc. As such, I'm not able to say much about it. –  Simon Gibbs Jan 16 '13 at 14:07
    
Then it's impossible to answer this, how are we supposed to know what it's doing. Also plugins still have to be GPL. Is it even using the HTTP API? –  Wyck Jan 16 '13 at 14:10
    
The question is about cUrl and WordPress coding practices. There is nothing uniquely clever behind the curtain (and nothing pretending to be clever on the outside either). We are not using the HTTP API, as the docs do not mention support for authentication. –  Simon Gibbs Jan 16 '13 at 14:16
2  
The HTTP API does support authentication via the $args parameter (though it is not documented), it's just a wrapper for cURL. By default cURL connections are persistent, have you set-up any HTTP debugging, I'm not sure what this question has to do with WordPress.Maybe you can clear up what you mean by "latency in the meta box". –  Wyck Jan 16 '13 at 14:44

2 Answers 2

up vote 2 down vote accepted

It is not possible, at least not within reason, to solve either of these issues due to fundamental limitations of PHP in a web envionment (mod_php).

Class variables do not persist between web requests, so the Keep-Alive part of the problem cannot be fixed because all the resources (i.e. curl handle, tcp socket) will be destroyed at the end of the request. Re-using the cUrl handle seem to be mostly relevant to batch scripts or the rare web-script that needs to access multiple URLs in one go. The kind of performance optimizations considered standard for Java developers are therefore unavailable to PHP developers.

One could try to implement digest authentication by sticking the values for the Authorization header into session, but that is not cheap to implement (high dev time) and not something that many enterprises want to dedicate time to. This might be a nice 3rd year university project for example.

Hopefully, at some point, someone will release an add-on product for PHP in Apache that somehow channels HTTP requests through to a TCP connection pool external to the process. That would probably make money, given that it will knock a lot of latency off upstream HTTP requests from PHP.

share|improve this answer
    
Have a look at socketo.me –  Wyck Jan 24 '13 at 20:05

Inspecting the cURL request

Even if inspecting and debugging a cURL request isn't something unique to WordPress, you need to know a good portion of the WP HTTP API internals to get around it. I've written myself a plugin to do that for me, which I've modified/stripped down and attached for you to use. It will output cURL object details in the shutdown hook, both in the admin UI as well as in the Front-End/Theme.

What you can do, is use the exact same hook http_api_curl and catch the cURL object. Then save it into a class variable, attach your next calls to it and go on. I haven't done anything like this myself before, but it should be doable.

Edit

As the number of edits here is limited, I moved the further development of this plugin to my GitHub account. If you have notes, comments or suggestions, leave a comment there.

The plugin below is fully functional. Still a much more advanced version is on GitHub.

<?php
/** 
 * Plugin Name: (#81791) Dump cURL Request & Response 
 * Author:      Franz Josef Kaiser
 */
add_action( 'plugins_loaded', array( 'WPSE81791_cURL', 'init' ) );
class WPSE81791_cURL
{
    protected static $instance;

    public static $dump;

    public static function init()
    {
        null === self :: $instance AND self :: $instance = new self;
        return self :: $instance;
    }

    public function __construct()
    {
        add_action( 'http_api_curl', array( $this, 'dump_curl' ) );
        add_action( 'shutdown', array( $this, 'do_dump' ) );
    }

    /**
     * Debug the response in the middle.
     * Catches the cURL object during the request.
     * @param  cURL $handle
     * @return void
     */
    public function dump_curl( &$handle )
    {
        curl_setopt( $handle, CURLINFO_HEADER_OUT, 1 );
        curl_setopt( $handle, CURLOPT_HEADER, 0 );
        curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'dump_curl_buffer_cb' ) );
        curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'dump_curl_buffer_cb' ) );
        curl_exec( $handle );
        $this->add_dump(
             curl_getinfo( $handle, CURLINFO_HEADER_OUT )
            .$this->dump_curl_buffer_cb( null )
            .'<br />Nr. of Errors: '.curl_errno( $handle )
            .'<br />Errors: '.curl_error( $handle )
        );
    }

    /**
     * Callback for cURL dump method
     * @param  object $curl
     * @param  null   $data
     * @return int
     */
    public function dump_curl_buffer_cb( $curl, $data = null )
    {
        static $buffer = '';
        if ( is_null( $curl ) )
        {
            $r = $buffer;
            $buffer = '';
            return $r;
        }
        $buffer .= $data;
        return strlen( $data );
    }

    /**
     * Adds data to the static data stack
     * @param  
     * @return void
     */
    public function add_dump( $data )
    {
        self :: $dump[] = $data;
    }

    /**
     * Dumps the data stack for debug
     * @param  
     * @return void
     */
    public function do_dump()
    {
        printf(
             '<pre>%s</pre>'
            ,var_export( implode( "<br />", self :: $dump ), true ) 
        );
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.