juce::URL::createInputStream() returns null on Linux


#1

I have the following simple code running on both Windows and Linux. On Windows it works just fine, however, on Linux the stream pointer results always set to null.

juce::URL request("www.google.com");
juce::InputStream* stream = request.createInputStream(true);

The same executable is accessing the Internet using Curl and no proxy or firewall is enabled.

Any help would be appreciated.


#2

You might want to include the scheme in the URL string, e.g. “http://” or “https://”


#3

Same thing with a URL scheme (probably over-simplified my test example :wink: )


#4

I just found this out in juce_core.h:

/** Config: JUCE_USE_CURL
    Enables http/https support via libcurl (Linux only). Enabling this will add an additional
    run-time dynmic dependency to libcurl.

    If you disable this then https/ssl support will not be available on linux.
*/
#ifndef JUCE_USE_CURL
 #define JUCE_USE_CURL 0
#endif

It does not seem to make a difference anyway, but would this mean that if my app config does not define JUCE_USE_CURL to 1 I may be getting what I see?


#5

Any idea about this from any developer?

Thank you.


#6

Do you mean that with JUCE_USE_CURL=1 it still doesn’t work? If so, maybe debug into what’s going on inside and look for clues…


#7

We definitely test this use case when JUCE_USE_CURL=1 is enabled. You can enable this flag by clicking on the juce_core module in the Projucer.


#8

Thanks for your feedback, I’ll make a clean build using that flag and let you know how it goes.

In the meantime, in the documentation of this method I could not find anything regarding to this, so it’s probably worth mentioning to avoid further confusion.

Also, the doc says:

Note that on some platforms (Android, for example) it’s not permitted to do any network action from the message thread, so you must only call it from a background thread.

This obviously affects Android, but what are those other platforms affected by this behavior? I am guessing this could also be the case.


#9

I should also mention that I am calling this method from an executable that uses it from a shared object, so I am guessing some of the CURL libraries may not be properly initialized for what I can see.


#10

Any news about this? This is forcing me to reimplement part of my code so it doesn’t use Juce as it doesn’t work on Linux not even using JUCE_USE_CURL=1


#11

We test this regularly on Linux - in fact the Projucer uses this internally. So you’ll need to give us some more hints about what your code is doing differently. Feel free to share a minimal code example which reproduces the problem.


#12

I’ve just tried this:

URL url ("https://www.google.com/");
ScopedPointer<InputStream> stream = url.createInputStream (false);

with JUCE_USE_CURL=1 and it’s working as expected.

Can you step through in a debugger to find out what’s happening?


#13

Sure, I’ll do that.

Just to make sure, did you test this as from binary that uses Juce as part of a shared object? This is the way I can reproduce it.


#14

After some debugging, I can see that in the constructor of WebInputStream (in juce_curl_Network.cpp), after calling connect the CUrl error in lastError is set to CURLE_SSL_CACERT_BADFILE.

(gdb) bt
#0  juce::WebInputStream::WebInputStream (this=0x81a540, address=..., isPost=true, postData=..., 
    progressCallback=0, progressCallbackContext=0x0, headers=..., timeOutMs=0, responseHeaders=0x0, 
    maxRedirects=5, httpRequest=...)
    at /home/bob/full/libraries/libjuce/modules/juce_core/native/juce_curl_Network.cpp:52
#1  0x00007ffff5b661de in juce::URL::createInputStream (this=0x7fffffffd780, usePostCommand=true, 
    progressCallback=0, progressCallbackContext=0x0, headers=..., timeOutMs=0, responseHeaders=0x0, 
    statusCode=0x0, numRedirectsToFollow=5, httpRequestCmd=...)
    at /home/bob/full/libraries/libjuce/modules/juce_core/network/juce_URL.cpp:359
...
(gdb) info args
this = 0x81a540
address = @0x7fffffffd710
isPost = true
postData = @0x7fffffffd6f0
progressCallback = 0
progressCallbackContext = 0x0
headers = @0x7fffffffd850
timeOutMs = 0
responseHeaders = 0x0
maxRedirects = 5
httpRequest = @0x7fffffffd840
(gdb) p address
$3 = (const juce::String &) @0x7fffffffd710: {static empty = {
    static empty = <same as static member of an already seen type>, text = {data = 0x7ffff6fee8a0 ""}}, 
  text = {data = 0x819e60 "https://google.com"}}
(gdb) p lastError
$4 = 77

I hope that helps.


#15

That sounds like an SSL configuration or linking error. Does it work OK if you’re not using a JUCE in a dynamic library?

I put this function into a dynamic JUCE library

void download()
{
    URL url ("https://google.com");
    ScopedPointer<InputStream> stream = url.createInputStream (true);

    if (stream == nullptr)
        DBG ("Oh :(");
    else
        DBG (stream->readEntireStreamAsString());
}

and used the following main

extern void download();

int main()
{
    download();
    return 0;
}

and everything worked as expected.


#16

When setting up curl on your machine, there is a ./configure parameter called --with-ca-path. Are you sure that you set this to the correct path where the default certificates are stored on your machine. Try JUCE on a distro with pre-installed curl, Ubuntu, for example.


#17

I though that too, but it doesn’t affect other other executables on the same machine, curl included, then I figured out the statically linked of libcurl and libcrypto that my shared object contains may be the issue as currently no CACERT is supported.

I’ll let you know if this is not the problem.

Just one thing, it would be great if you would return an error message or code instead of just returning null as this would have help me identifying the issue and wouldn’t have bothered you with my rants :wink:

Thank you for looking into this.