Repository: conan-io/conan

Issue:
Using "?" in a tools.get() URL will fail, while using it in a tools.download() will succeed.
To help us debug your issue please explain:

- [x] I've read the [CONTRIBUTING guide](https://raw.githubusercontent.com/conan-io/conan/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.

Example:
```
from conans import ConanFile, tools

class LibraryConan(ConanFile):
    name = "Library"
    version = "1.0"
    license = ""
    url = "<Package recipe repository url here, for issues about the package>"
    description = ""
    settings = "os", "compiler", "build_type", "arch"
    
    def source(self):
        tools.get("http://example.com/?file=1")
```
```
conan create . Company/beta
ERROR: Library/1.0@Company/beta: Error in source() method, line 12
        tools.get("http://example.com/?file=1")
        ConanConnectionError: Download failed, check server, possibly try again
[Errno 22] Invalid argument: 'C:\\Users\\User\\.conan\\data\\Library\\1.0\\Company\\beta\\source\\?file=1'
```



Discussion:
Verified: ``tools.get()`` uses the last part of the URL as the name of the file to be saved.

Shoud it use just a temporary random filename? The given one stripped the special characters?

Resolving patch:
diff --git a/conans/client/tools/net.py b/conans/client/tools/net.py
--- a/conans/client/tools/net.py
+++ b/conans/client/tools/net.py
@@ -9,10 +9,13 @@
 _global_requester = None
 
 
-def get(url, md5='', sha1='', sha256='', destination="."):
+def get(url, md5='', sha1='', sha256='', destination=".", filename=""):
     """ high level downloader + unzipper + (optional hash checker) + delete temporary zip
     """
-    filename = os.path.basename(url)
+    if not filename and ("?" in url or "=" in url):
+        raise ConanException("Cannot deduce file name form url. Use 'filename' parameter.")
+
+    filename = filename or os.path.basename(url)
     download(url, filename)
 
     if md5:


Test patch:
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-
+from bottle import static_file, request
 import mock
 import os
 import platform
@@ -25,7 +25,8 @@
 
 from conans.test.utils.runner import TestRunner
 from conans.test.utils.test_files import temp_folder
-from conans.test.utils.tools import TestClient, TestBufferConanOutput, create_local_git_repo
+from conans.test.utils.tools import TestClient, TestBufferConanOutput, create_local_git_repo, \
+    StoppableThreadBottle
 
 from conans.tools import which
 from conans.tools import OSInfo, SystemPackageTool, replace_in_file, AptTool, ChocolateyTool,\
@@ -33,6 +34,8 @@
 from conans.util.files import save, load, md5
 import requests
 
+from nose.plugins.attrib import attr
+
 
 class SystemPackageToolTest(unittest.TestCase):
     def setUp(self):
@@ -993,6 +996,51 @@ def detect_windows_subsystem_test(self):
         else:
             self.assertEqual(str, type(result))
 
+    @attr('slow')
+    def get_filename_download_test(self):
+        # Create a tar file to be downloaded from server
+        with tools.chdir(tools.mkdir_tmp()):
+            import tarfile
+            tar_file = tarfile.open("sample.tar.gz", "w:gz")
+            tools.mkdir("test_folder")
+            tar_file.add(os.path.abspath("test_folder"), "test_folder")
+            tar_file.close()
+            file_path = os.path.abspath("sample.tar.gz")
+            assert(os.path.exists(file_path))
+
+        # Instance stoppable thread server and add endpoints
+        thread = StoppableThreadBottle()
+
+        @thread.server.get("/this_is_not_the_file_name")
+        def get_file():
+            return static_file(os.path.basename(file_path), root=os.path.dirname(file_path))
+
+        @thread.server.get("/")
+        def get_file2():
+            self.assertEquals(request.query["file"], "1")
+            return static_file(os.path.basename(file_path), root=os.path.dirname(file_path))
+
+        thread.run_server()
+
+        # Test: File name cannot be deduced from '?file=1'
+        with self.assertRaisesRegexp(ConanException,
+                                     "Cannot deduce file name form url. Use 'filename' parameter."):
+            tools.get("http://localhost:8266/?file=1")
+
+        # Test: Works with filename parameter instead of '?file=1'
+        with tools.chdir(tools.mkdir_tmp()):
+            tools.get("http://localhost:8266/?file=1", filename="sample.tar.gz")
+            self.assertTrue(os.path.exists("test_folder"))
+
+        # Test: Use a different endpoint but still not the filename one
+        with tools.chdir(tools.mkdir_tmp()):
+            from zipfile import BadZipfile
+            with self.assertRaises(BadZipfile):
+                tools.get("http://localhost:8266/this_is_not_the_file_name")
+            tools.get("http://localhost:8266/this_is_not_the_file_name", filename="sample.tar.gz")
+            self.assertTrue(os.path.exists("test_folder"))
+        thread.stop()
+
 
 class GitToolTest(unittest.TestCase):
 
@@ -1059,10 +1107,10 @@ def test_clone_submodule_git(self):
         def _create_paths():
             tmp = temp_folder()
             submodule_path = os.path.join(
-                tmp, 
+                tmp,
                 os.path.basename(os.path.normpath(submodule)))
             subsubmodule_path = os.path.join(
-                submodule_path, 
+                submodule_path,
                 os.path.basename(os.path.normpath(subsubmodule)))
             return tmp, submodule_path, subsubmodule_path
 
@@ -1079,7 +1127,7 @@ def _create_paths():
         with self.assertRaisesRegexp(ConanException, "Invalid 'submodule' attribute value in the 'scm'."):
             git.clone(path, submodule="invalid")
 
-        # Check shallow 
+        # Check shallow
         tmp, submodule_path, subsubmodule_path = _create_paths()
         git = Git(tmp)
         git.clone(path, submodule="shallow")
diff --git a/conans/test/utils/tools.py b/conans/test/utils/tools.py
--- a/conans/test/utils/tools.py
+++ b/conans/test/utils/tools.py
@@ -2,13 +2,16 @@
 import shlex
 import shutil
 import sys
+import threading
 import uuid
 from collections import Counter
 from contextlib import contextmanager
 from io import StringIO
 
+import bottle
 import requests
 import six
+import time
 from mock import Mock
 from six.moves.urllib.parse import urlsplit, urlunsplit
 from webtest.app import TestApp
@@ -507,3 +510,24 @@ def save(self, files, path=None, clean_first=False):
         save_files(path, files)
         if not files:
             mkdir(self.current_folder)
+
+
+class StoppableThreadBottle(threading.Thread):
+    """
+    Real server to test download endpoints
+    """
+    server = None
+
+    def __init__(self, host="127.0.0.1", port=8266):
+        self.server = bottle.Bottle()
+        super(StoppableThreadBottle, self).__init__(target=self.server.run, kwargs={"host": host,
+                                                                                    "port": port})
+        self.daemon = True
+        self._stop = threading.Event()
+
+    def stop(self):
+        self._stop.set()
+
+    def run_server(self):
+        self.start()
+        time.sleep(1)