Repository: conan-io/conan

Issue:
Search query `compiler.libcxx` seems broken
When adding `compiler.libcxx=libstdc++` to my search query it never matches any packages:

```
tru@xps ~/Code/Plex/plex-conan$ conan search -r plex zlib/1.2.8@plex/stable -q "os=Linux"
Existing packages for recipe zlib/1.2.8@plex/stable:

    Package_ID: 2f48fd86f2c789d46ce8ba025775628e47b36354
        [options]
            shared: False
        [settings]
            arch: x86_64
            build_type: Release
            compiler: gcc
            compiler.libcxx: libstdc++11
            compiler.version: 6.2
            os: Linux
        [requires]
            plexconantool/1.0@plex/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9
        outdated from recipe: False

    Package_ID: 60c8d7f7b9f3a84a70cc433b2769f5324f190d5f
        [options]
            shared: False
        [settings]
            arch: x86_64
            build_type: Release
            compiler: gcc
            compiler.libcxx: libstdc++
            compiler.version: 6.2
            os: Linux
        [requires]
            plexconantool/1.0@plex/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9
        outdated from recipe: False

    Package_ID: 74110c4f5bcaa8e2e9536ddbb6057226e2c675af
        [options]
            shared: False
        [settings]
            arch: x86_64
            build_type: Release
            compiler: gcc
            compiler.libcxx: libstdc++
            compiler.version: 5.4
            os: Linux
        [requires]
            plexconantool/1.0@plex/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9
        outdated from recipe: False
```

```
tru@xps ~/Code/Plex/plex-conan$ conan search -r plex zlib/1.2.8@plex/stable -q "compiler.libcxx=libstdc++11"
There are no packages for reference 'zlib/1.2.8@plex/stable' matching the query 'compiler.libcxx=libstdc++11'
```


Discussion:
Looks like a bug. Let's investigate

Resolving patch:
diff --git a/conans/client/rest/rest_client.py b/conans/client/rest/rest_client.py
--- a/conans/client/rest/rest_client.py
+++ b/conans/client/rest/rest_client.py
@@ -11,7 +11,7 @@
 from conans.model.manifest import FileTreeManifest
 from conans.client.rest.uploader_downloader import Uploader, Downloader
 from conans.model.ref import ConanFileReference
-from six.moves.urllib.parse import urlsplit, parse_qs
+from six.moves.urllib.parse import urlsplit, parse_qs, urlencode
 
 
 def handle_return_deserializer(deserializer=None):
@@ -218,19 +218,20 @@ def search(self, pattern=None, ignorecase=True):
         """
         query = ''
         if pattern:
-            case_sensitive = "&ignorecase=False" if not ignorecase else ''
-            pattern = "q=%s" % pattern
-            query = "?%s%s" % (pattern, case_sensitive)
+            params = {"q": pattern}
+            if not ignorecase:
+                params["ignorecase"] = "False"
+            query = "?%s" % urlencode(params)
 
         url = "%s/conans/search%s" % (self._remote_api_url, query)
         response = self._get_json(url)["results"]
         return [ConanFileReference.loads(ref) for ref in response]
 
     def search_packages(self, reference, query):
-        url = "%s/conans/%s/search" % (self._remote_api_url, "/".join(reference))
+        url = "%s/conans/%s/search?" % (self._remote_api_url, "/".join(reference))
         if query:
-            url += "?q=%s" % query
-
+            params = {"q": query}
+            url += urlencode(params)
         references = self._get_json(url)
         return references
 


Test patch:
diff --git a/conans/test/command/search_test.py b/conans/test/command/search_test.py
--- a/conans/test/command/search_test.py
+++ b/conans/test/command/search_test.py
@@ -1,10 +1,10 @@
 import unittest
-from conans.test.tools import TestClient
+from conans.test.tools import TestClient, TestServer
 from conans.paths import PACKAGES_FOLDER, CONANINFO, EXPORT_FOLDER,\
     CONAN_MANIFEST
 import os
 from conans.model.manifest import FileTreeManifest
-from time import time
+import shutil
 
 
 conan_vars1 = '''
@@ -26,6 +26,7 @@
     arch=x86
     compiler=gcc
     compiler.version=4.3
+    compiler.libcxx=libstdc++
 [options]
     use_Qt=True
 '''
@@ -36,6 +37,7 @@
     arch=x86
     compiler=gcc
     compiler.version=4.5
+    compiler.libcxx=libstdc++11
 [options]
     use_Qt=False
 [full_requires]
@@ -79,7 +81,8 @@
 class SearchTest(unittest.TestCase):
 
     def setUp(self):
-        self.client = TestClient()
+        self.servers = {"local": TestServer()}
+        self.client = TestClient(servers=self.servers)
 
         # No conans created
         self.client.run("search")
@@ -87,20 +90,20 @@ def setUp(self):
         self.assertIn('There are no packages', output)
 
         # Conans with and without packages created
-        root_folder1 = 'Hello/1.4.10/fenix/testing'
+        self.root_folder1 = 'Hello/1.4.10/fenix/testing'
         root_folder2 = 'helloTest/1.4.10/fenix/stable'
         root_folder3 = 'Bye/0.14/fenix/testing'
         root_folder4 = 'NodeInfo/1.0.2/fenix/stable'
         root_folder5 = 'MissFile/1.0.2/fenix/stable'
 
         self.client.save({"Empty/1.10/fake/test/reg/fake.txt": "//",
-                          "%s/%s/WindowsPackageSHA/%s" % (root_folder1,
+                          "%s/%s/WindowsPackageSHA/%s" % (self.root_folder1,
                                                           PACKAGES_FOLDER,
                                                           CONANINFO): conan_vars1,
-                          "%s/%s/PlatformIndependantSHA/%s" % (root_folder1,
+                          "%s/%s/PlatformIndependantSHA/%s" % (self.root_folder1,
                                                                PACKAGES_FOLDER,
                                                                CONANINFO): conan_vars1b,
-                          "%s/%s/LinuxPackageSHA/%s" % (root_folder1,
+                          "%s/%s/LinuxPackageSHA/%s" % (self.root_folder1,
                                                         PACKAGES_FOLDER,
                                                         CONANINFO): conan_vars1c,
                           "%s/%s/a44f541cd44w57/%s" % (root_folder2,
@@ -119,7 +122,7 @@ def setUp(self):
 
         # Fake some manifests to be able to calculate recipe hash
         fake_manifest = FileTreeManifest(1212, {})
-        self.client.save({os.path.join(root_folder1, EXPORT_FOLDER, CONAN_MANIFEST): str(fake_manifest),
+        self.client.save({os.path.join(self.root_folder1, EXPORT_FOLDER, CONAN_MANIFEST): str(fake_manifest),
                           os.path.join(root_folder2, EXPORT_FOLDER, CONAN_MANIFEST): str(fake_manifest),
                           os.path.join(root_folder3, EXPORT_FOLDER, CONAN_MANIFEST): str(fake_manifest),
                           os.path.join(root_folder4, EXPORT_FOLDER, CONAN_MANIFEST): str(fake_manifest),
@@ -151,6 +154,31 @@ def package_search_with_empty_query_test(self):
         self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
         self.assertIn("LinuxPackageSHA", self.client.user_io.out)
 
+    def package_search_nonescaped_characters_test(self):
+        self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++11"')
+        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+
+        self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++"')
+        self.assertNotIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+
+        # Now search with a remote
+        os.rmdir(self.servers["local"].paths.store)
+        shutil.copytree(self.client.paths.store, self.servers["local"].paths.store)
+        self.client.run("remove Hello* -f")
+        self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++11" -r local')
+        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+
+        self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++" -r local')
+        self.assertNotIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+
     def package_search_with_invalid_query_test(self):
         self.client.run("search Hello/1.4.10/fenix/testing -q 'invalid'", ignore_error=True)
         self.assertIn("Invalid package query: invalid", self.client.user_io.out)
@@ -195,6 +223,7 @@ def package_search_properties_filter_test(self):
         [settings]
             arch: x86
             compiler: gcc
+            compiler.libcxx: libstdc++11
             compiler.version: 4.5
             os: Linux
         [requires]
@@ -209,6 +238,7 @@ def package_search_properties_filter_test(self):
         [settings]
             arch: x86
             compiler: gcc
+            compiler.libcxx: libstdc++
             compiler.version: 4.3
         outdated from recipe: True