diff --git a/gns3server/controller/symbols.py b/gns3server/controller/symbols.py
index 5e787fa7..9549507d 100644
--- a/gns3server/controller/symbols.py
+++ b/gns3server/controller/symbols.py
@@ -40,9 +40,10 @@ class Symbols:
'symbol_id': symbol_id,
'filename': file,
'builtin': True,
- 'url': '/static/builtin_symbols/' + file
})
self._symbols_path[symbol_id] = os.path.join(get_resource("symbols"), file)
+ symbols.sort(key=lambda x: x["filename"])
+
#TODO: support ~/GNS3/symbols directory
return symbols
diff --git a/gns3server/handlers/__init__.py b/gns3server/handlers/__init__.py
index 51b9fc07..e918c0a8 100644
--- a/gns3server/handlers/__init__.py
+++ b/gns3server/handlers/__init__.py
@@ -16,7 +16,6 @@
from gns3server.handlers.index_handler import IndexHandler
-from gns3server.handlers.static_handler import StaticHandler
from gns3server.handlers.api.controller import *
diff --git a/gns3server/handlers/api/controller/symbol_handler.py b/gns3server/handlers/api/controller/symbol_handler.py
index 1f5ddc6f..7ede100f 100644
--- a/gns3server/handlers/api/controller/symbol_handler.py
+++ b/gns3server/handlers/api/controller/symbol_handler.py
@@ -36,3 +36,17 @@ class SymbolHandler:
controller = Controller.instance()
response.json(controller.symbols.list())
+
+ @Route.get(
+ r"/symbols/{symbol_id:.+}/raw",
+ description="Get the symbol file",
+ status_codes={
+ 200: "Symbol returned"
+ })
+ def raw(request, response):
+
+ controller = Controller.instance()
+ try:
+ yield from response.file(controller.symbols.get_path(request.match_info["symbol_id"]))
+ except KeyError:
+ response.set_status(404)
diff --git a/gns3server/handlers/static_handler.py b/gns3server/handlers/static_handler.py
deleted file mode 100644
index c8e09b48..00000000
--- a/gns3server/handlers/static_handler.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Copyright (C) 2016 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# 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, see .
-
-import os
-import asyncio
-import mimetypes
-from aiohttp import hdrs
-
-from gns3server.web.route import Route
-from gns3server.utils.get_resource import get_resource
-
-
-class StaticHandler:
-
- @Route.get(
- r"/static/{type}/{path:.+}",
- description="Serve static content from various locations"
- )
- def get(request, response):
- type = request.match_info["type"]
- # CLeanup the path for security
- path = os.path.normpath(request.match_info["path"]).strip('/.')
- if type == "builtin_symbols":
- try:
- yield from StaticHandler._serve_file(os.path.join(get_resource("symbols"), path), request, response)
- except OSError:
- response.set_status(404)
-
- @asyncio.coroutine
- def _serve_file(path, request, response):
- ct, encoding = mimetypes.guess_type(path)
- if not ct:
- ct = 'application/octet-stream'
- if encoding:
- response.headers[hdrs.CONTENT_ENCODING] = encoding
- response.content_type = ct
-
- st = os.stat(path)
- response.last_modified = st.st_mtime
- response.content_length = st.st_size
-
- with open(path, 'rb') as fobj:
- response.start(request)
- chunk_size = 4096
- chunk = fobj.read(chunk_size)
- while chunk:
- response.write(chunk)
- yield from response.drain()
- chunk = fobj.read(chunk_size)
-
- if chunk:
- response.write(chunk[:count])
- yield from response.drain()
diff --git a/gns3server/web/response.py b/gns3server/web/response.py
index 5d5a11ed..c057c6a9 100644
--- a/gns3server/web/response.py
+++ b/gns3server/web/response.py
@@ -17,11 +17,14 @@
import json
import jsonschema
+import aiohttp
import aiohttp.web
+import mimetypes
import asyncio
import logging
-import sys
import jinja2
+import sys
+import os
from ..utils.get_resource import get_resource
from ..version import __version__
@@ -102,6 +105,35 @@ class Response(aiohttp.web.Response):
raise aiohttp.web.HTTPBadRequest(text="{}".format(e))
self.body = json.dumps(answer, indent=4, sort_keys=True).encode('utf-8')
+ @asyncio.coroutine
+ def file(self, path):
+ """
+ Return a file as a response
+ """
+ ct, encoding = mimetypes.guess_type(path)
+ if not ct:
+ ct = 'application/octet-stream'
+ if encoding:
+ self.headers[aiohttp.hdrs.CONTENT_ENCODING] = encoding
+ self.content_type = ct
+
+ st = os.stat(path)
+ self.last_modified = st.st_mtime
+ self.content_length = st.st_size
+
+ with open(path, 'rb') as fobj:
+ self.start(self._request)
+ chunk_size = 4096
+ chunk = fobj.read(chunk_size)
+ while chunk:
+ self.write(chunk)
+ yield from self.drain()
+ chunk = fobj.read(chunk_size)
+
+ if chunk:
+ self.write(chunk[:count])
+ yield from self.drain()
+
def redirect(self, url):
"""
Redirect to url
diff --git a/tests/controller/test_symbols.py b/tests/controller/test_symbols.py
index 94ef315d..563d0441 100644
--- a/tests/controller/test_symbols.py
+++ b/tests/controller/test_symbols.py
@@ -26,8 +26,12 @@ def test_list():
symbols = Symbols()
assert {
'symbol_id': ':/symbols/firewall.svg',
- 'url': '/static/builtin_symbols/firewall.svg',
'filename': 'firewall.svg',
'builtin': True
} in symbols.list()
assert symbols
+
+
+def test_get_path():
+ symbols = Symbols()
+ assert symbols.get_path(':/symbols/firewall.svg') == get_resource("symbols/firewall.svg")
diff --git a/tests/handlers/api/controller/test_symbol.py b/tests/handlers/api/controller/test_symbol.py
index 23c8d483..04483887 100644
--- a/tests/handlers/api/controller/test_symbol.py
+++ b/tests/handlers/api/controller/test_symbol.py
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import urllib.parse
+
from gns3server.config import Config
@@ -23,7 +25,17 @@ def test_symbols(http_controller):
assert response.status == 200
assert {
'symbol_id': ':/symbols/firewall.svg',
- 'url': '/static/builtin_symbols/firewall.svg',
'filename': 'firewall.svg',
'builtin': True
} in response.json
+
+
+def test_get(http_controller):
+ response = http_controller.get('/symbols/' + urllib.parse.quote(':/symbols/firewall.svg') + '/raw')
+ assert response.status == 200
+ assert response.headers['CONTENT-LENGTH'] == '9381'
+ assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
+ assert '' in response.html
+
+ response = http_controller.get('/symbols/404.png/raw')
+ assert response.status == 404
diff --git a/tests/handlers/test_static.py b/tests/handlers/test_static.py
deleted file mode 100644
index 4ae8746f..00000000
--- a/tests/handlers/test_static.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2015 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# 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, see .
-
-
-import aiohttp
-import os
-from unittest.mock import patch
-
-
-def test_get(http_root):
- response = http_root.get('/static/builtin_symbols/firewall.svg')
- assert response.status == 200
- assert response.headers['CONTENT-LENGTH'] == '9381'
- assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
- assert '' in response.html
-
- response = http_root.get('/static/builtin_symbols/../main.py')
- assert response.status == 404
-
- response = http_root.get('/static/builtin_symbols/404.png')
- assert response.status == 404