MapProxy 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MapProxy-2.1.0.dist-info/AUTHORS.txt +33 -0
- MapProxy-2.1.0.dist-info/COPYING.txt +60 -0
- MapProxy-2.1.0.dist-info/LICENSE.txt +202 -0
- MapProxy-2.1.0.dist-info/METADATA +165 -0
- MapProxy-2.1.0.dist-info/RECORD +459 -0
- MapProxy-2.1.0.dist-info/WHEEL +5 -0
- MapProxy-2.1.0.dist-info/entry_points.txt +4 -0
- MapProxy-2.1.0.dist-info/top_level.txt +1 -0
- mapproxy/__init__.py +0 -0
- mapproxy/cache/__init__.py +36 -0
- mapproxy/cache/azureblob.py +145 -0
- mapproxy/cache/base.py +120 -0
- mapproxy/cache/compact.py +665 -0
- mapproxy/cache/couchdb.py +301 -0
- mapproxy/cache/dummy.py +36 -0
- mapproxy/cache/file.py +200 -0
- mapproxy/cache/geopackage.py +647 -0
- mapproxy/cache/legend.py +87 -0
- mapproxy/cache/mbtiles.py +411 -0
- mapproxy/cache/meta.py +80 -0
- mapproxy/cache/path.py +261 -0
- mapproxy/cache/redis.py +152 -0
- mapproxy/cache/renderd.py +100 -0
- mapproxy/cache/riak.py +206 -0
- mapproxy/cache/s3.py +209 -0
- mapproxy/cache/tile.py +736 -0
- mapproxy/client/__init__.py +0 -0
- mapproxy/client/arcgis.py +82 -0
- mapproxy/client/cgi.py +141 -0
- mapproxy/client/http.py +295 -0
- mapproxy/client/log.py +33 -0
- mapproxy/client/tile.py +158 -0
- mapproxy/client/wms.py +255 -0
- mapproxy/compat/__init__.py +0 -0
- mapproxy/compat/image.py +86 -0
- mapproxy/config/__init__.py +22 -0
- mapproxy/config/config-schema.json +813 -0
- mapproxy/config/config.py +213 -0
- mapproxy/config/coverage.py +108 -0
- mapproxy/config/defaults.py +102 -0
- mapproxy/config/loader.py +2399 -0
- mapproxy/config/spec.py +657 -0
- mapproxy/config/validator.py +242 -0
- mapproxy/config_template/__init__.py +0 -0
- mapproxy/config_template/base_config/config.wsgi +10 -0
- mapproxy/config_template/base_config/full_example.yaml +598 -0
- mapproxy/config_template/base_config/full_seed_example.yaml +79 -0
- mapproxy/config_template/base_config/log.ini +35 -0
- mapproxy/config_template/base_config/mapproxy.yaml +60 -0
- mapproxy/config_template/base_config/seed.yaml +27 -0
- mapproxy/exception.py +149 -0
- mapproxy/featureinfo.py +251 -0
- mapproxy/grid.py +1199 -0
- mapproxy/image/__init__.py +549 -0
- mapproxy/image/fonts/DejaVuSans.ttf +0 -0
- mapproxy/image/fonts/DejaVuSansMono.ttf +0 -0
- mapproxy/image/fonts/LICENSE +99 -0
- mapproxy/image/fonts/__init__.py +0 -0
- mapproxy/image/mask.py +79 -0
- mapproxy/image/merge.py +323 -0
- mapproxy/image/message.py +357 -0
- mapproxy/image/opts.py +185 -0
- mapproxy/image/tile.py +171 -0
- mapproxy/image/transform.py +350 -0
- mapproxy/layer.py +489 -0
- mapproxy/multiapp.py +230 -0
- mapproxy/proj.py +309 -0
- mapproxy/request/__init__.py +18 -0
- mapproxy/request/arcgis.py +268 -0
- mapproxy/request/base.py +466 -0
- mapproxy/request/tile.py +131 -0
- mapproxy/request/wms/__init__.py +829 -0
- mapproxy/request/wms/exception.py +107 -0
- mapproxy/request/wmts.py +442 -0
- mapproxy/response.py +237 -0
- mapproxy/script/__init__.py +0 -0
- mapproxy/script/conf/__init__.py +0 -0
- mapproxy/script/conf/app.py +222 -0
- mapproxy/script/conf/caches.py +44 -0
- mapproxy/script/conf/geopackage.py +136 -0
- mapproxy/script/conf/layers.py +54 -0
- mapproxy/script/conf/seeds.py +36 -0
- mapproxy/script/conf/sources.py +88 -0
- mapproxy/script/conf/utils.py +148 -0
- mapproxy/script/defrag.py +187 -0
- mapproxy/script/export.py +337 -0
- mapproxy/script/grids.py +198 -0
- mapproxy/script/scales.py +134 -0
- mapproxy/script/util.py +410 -0
- mapproxy/script/wms_capabilities.py +160 -0
- mapproxy/seed/__init__.py +0 -0
- mapproxy/seed/cachelock.py +127 -0
- mapproxy/seed/cleanup.py +191 -0
- mapproxy/seed/config.py +481 -0
- mapproxy/seed/script.py +391 -0
- mapproxy/seed/seeder.py +551 -0
- mapproxy/seed/spec.py +66 -0
- mapproxy/seed/util.py +266 -0
- mapproxy/service/__init__.py +14 -0
- mapproxy/service/base.py +45 -0
- mapproxy/service/demo.py +364 -0
- mapproxy/service/kml.py +333 -0
- mapproxy/service/ows.py +39 -0
- mapproxy/service/template_helper.py +55 -0
- mapproxy/service/templates/demo/capabilities_demo.html +18 -0
- mapproxy/service/templates/demo/demo.html +181 -0
- mapproxy/service/templates/demo/openlayers-demo.cfg +16 -0
- mapproxy/service/templates/demo/static/img/blank.gif +0 -0
- mapproxy/service/templates/demo/static/img/east-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/north-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/south-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/west-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-minus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-plus-mini.png +0 -0
- mapproxy/service/templates/demo/static/img/zoom-world-mini.png +0 -0
- mapproxy/service/templates/demo/static/logo.png +0 -0
- mapproxy/service/templates/demo/static/ol.css +345 -0
- mapproxy/service/templates/demo/static/ol.js +4 -0
- mapproxy/service/templates/demo/static/proj4.min.js +1 -0
- mapproxy/service/templates/demo/static/proj4defs.js +1 -0
- mapproxy/service/templates/demo/static/site.css +137 -0
- mapproxy/service/templates/demo/static/theme/default/framedCloud.css +0 -0
- mapproxy/service/templates/demo/static/theme/default/google.css +17 -0
- mapproxy/service/templates/demo/static/theme/default/ie6-style.css +10 -0
- mapproxy/service/templates/demo/static/theme/default/style.css +482 -0
- mapproxy/service/templates/demo/static.html +34 -0
- mapproxy/service/templates/demo/tms_demo.html +117 -0
- mapproxy/service/templates/demo/wms_demo.html +144 -0
- mapproxy/service/templates/demo/wmts_demo.html +118 -0
- mapproxy/service/templates/tms_capabilities.xml +13 -0
- mapproxy/service/templates/tms_exception.xml +4 -0
- mapproxy/service/templates/tms_root_resource.xml +7 -0
- mapproxy/service/templates/tms_tilemap_capabilities.xml +14 -0
- mapproxy/service/templates/wms100capabilities.xml +112 -0
- mapproxy/service/templates/wms100exception.xml +4 -0
- mapproxy/service/templates/wms110capabilities.xml +152 -0
- mapproxy/service/templates/wms110exception.xml +5 -0
- mapproxy/service/templates/wms111capabilities.xml +183 -0
- mapproxy/service/templates/wms111exception.xml +5 -0
- mapproxy/service/templates/wms130capabilities.xml +326 -0
- mapproxy/service/templates/wms130exception.xml +8 -0
- mapproxy/service/templates/wmts100capabilities.xml +155 -0
- mapproxy/service/templates/wmts100exception.xml +9 -0
- mapproxy/service/tile.py +540 -0
- mapproxy/service/wms.py +868 -0
- mapproxy/service/wmts.py +387 -0
- mapproxy/source/__init__.py +83 -0
- mapproxy/source/arcgis.py +39 -0
- mapproxy/source/error.py +40 -0
- mapproxy/source/mapnik.py +262 -0
- mapproxy/source/tile.py +97 -0
- mapproxy/source/wms.py +273 -0
- mapproxy/srs.py +734 -0
- mapproxy/template.py +54 -0
- mapproxy/test/__init__.py +0 -0
- mapproxy/test/conftest.py +8 -0
- mapproxy/test/helper.py +255 -0
- mapproxy/test/http.py +511 -0
- mapproxy/test/image.py +219 -0
- mapproxy/test/mocker.py +2291 -0
- mapproxy/test/schemas/inspire/common/1.0/common.xsd +1461 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_bul.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_cze.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dan.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_dut.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_eng.xsd +155 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_est.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fin.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_fre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ger.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gle.xsd +109 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_gre.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_hun.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_ita.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lav.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_lit.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_mlt.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_pol.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_por.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_rum.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slo.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_slv.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_spa.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/enums/enum_swe.xsd +108 -0
- mapproxy/test/schemas/inspire/common/1.0/network.xsd +521 -0
- mapproxy/test/schemas/inspire/inspire_vs/1.0/inspire_vs.xsd +19 -0
- mapproxy/test/schemas/kml/2.2.0/ReadMe.txt +14 -0
- mapproxy/test/schemas/kml/2.2.0/atom-author-link.xsd +66 -0
- mapproxy/test/schemas/kml/2.2.0/ogckml22.xsd +1646 -0
- mapproxy/test/schemas/kml/2.2.0/xAL.xsd +1680 -0
- mapproxy/test/schemas/ows/1.1.0/ReadMe.txt +87 -0
- mapproxy/test/schemas/ows/1.1.0/ows19115subset.xsd +235 -0
- mapproxy/test/schemas/ows/1.1.0/owsAll.xsd +23 -0
- mapproxy/test/schemas/ows/1.1.0/owsCommon.xsd +157 -0
- mapproxy/test/schemas/ows/1.1.0/owsContents.xsd +86 -0
- mapproxy/test/schemas/ows/1.1.0/owsDataIdentification.xsd +127 -0
- mapproxy/test/schemas/ows/1.1.0/owsDomainType.xsd +279 -0
- mapproxy/test/schemas/ows/1.1.0/owsExceptionReport.xsd +76 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetCapabilities.xsd +112 -0
- mapproxy/test/schemas/ows/1.1.0/owsGetResourceByID.xsd +51 -0
- mapproxy/test/schemas/ows/1.1.0/owsInputOutputData.xsd +59 -0
- mapproxy/test/schemas/ows/1.1.0/owsManifest.xsd +125 -0
- mapproxy/test/schemas/ows/1.1.0/owsOperationsMetadata.xsd +140 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceIdentification.xsd +60 -0
- mapproxy/test/schemas/ows/1.1.0/owsServiceProvider.xsd +47 -0
- mapproxy/test/schemas/sld/1.1.0/sld_capabilities.xsd +27 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.dtd +353 -0
- mapproxy/test/schemas/wms/1.0.0/capabilities_1_0_0.xml +188 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.dtd +524 -0
- mapproxy/test/schemas/wms/1.0.7/capabilities_1_0_7.xml +260 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.dtd +273 -0
- mapproxy/test/schemas/wms/1.1.0/capabilities_1_1_0.xml +303 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.0/exception_1_1_0.xml +33 -0
- mapproxy/test/schemas/wms/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd +274 -0
- mapproxy/test/schemas/wms/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wms/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wms/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wms/1.3.0/ReadMe.txt +8 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xml +277 -0
- mapproxy/test/schemas/wms/1.3.0/capabilities_1_3_0.xsd +611 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xml +34 -0
- mapproxy/test/schemas/wms/1.3.0/exceptions_1_3_0.xsd +28 -0
- mapproxy/test/schemas/wmsc/1.1.1/OGC-exception.xsd +68 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_DescribeLayerResponse.dtd +22 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_MS_Capabilities.dtd +283 -0
- mapproxy/test/schemas/wmsc/1.1.1/WMS_exception_1_1_1.dtd +5 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.dtd +276 -0
- mapproxy/test/schemas/wmsc/1.1.1/capabilities_1_1_1.xml +303 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.dtd +6 -0
- mapproxy/test/schemas/wmsc/1.1.1/exception_1_1_1.xml +33 -0
- mapproxy/test/schemas/wmts/1.0/ReadMe.txt +32 -0
- mapproxy/test/schemas/wmts/1.0/wmts.xsd +28 -0
- mapproxy/test/schemas/wmts/1.0/wmtsAbstract.wsdl +151 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_request.xsd +38 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetCapabilities_response.xsd +564 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_request.xsd +57 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetFeatureInfo_response.xsd +72 -0
- mapproxy/test/schemas/wmts/1.0/wmtsGetTile_request.xsd +91 -0
- mapproxy/test/schemas/wmts/1.0/wmtsKVP.xsd +76 -0
- mapproxy/test/schemas/wmts/1.0/wmtsPayload_response.xsd +70 -0
- mapproxy/test/schemas/xlink/1.0.0/ReadMe.txt +6 -0
- mapproxy/test/schemas/xlink/1.0.0/xlinks.xsd +122 -0
- mapproxy/test/schemas/xml.xsd +287 -0
- mapproxy/test/system/__init__.py +98 -0
- mapproxy/test/system/fixture/arcgis.yaml +57 -0
- mapproxy/test/system/fixture/auth.yaml +70 -0
- mapproxy/test/system/fixture/cache.mbtiles +0 -0
- mapproxy/test/system/fixture/cache_azureblob.yaml +59 -0
- mapproxy/test/system/fixture/cache_band_merge.yaml +73 -0
- mapproxy/test/system/fixture/cache_bulk_meta_tiles.yaml +24 -0
- mapproxy/test/system/fixture/cache_coverage.yaml +84 -0
- mapproxy/test/system/fixture/cache_data/dop_cache_EPSG3857/00/000/000/000/000/000/000.png +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_EPSG900913/01/000/000/000/000/000/001.jpeg +0 -0
- mapproxy/test/system/fixture/cache_data/wms_cache_transparent_EPSG900913/01/000/000/000/000/000/001.png +0 -0
- mapproxy/test/system/fixture/cache_geopackage.yaml +56 -0
- mapproxy/test/system/fixture/cache_grid_names.yaml +50 -0
- mapproxy/test/system/fixture/cache_mbtiles.yaml +28 -0
- mapproxy/test/system/fixture/cache_s3.yaml +58 -0
- mapproxy/test/system/fixture/cache_source.yaml +81 -0
- mapproxy/test/system/fixture/combined_sources.yaml +130 -0
- mapproxy/test/system/fixture/coverage.yaml +77 -0
- mapproxy/test/system/fixture/demo.yaml +135 -0
- mapproxy/test/system/fixture/dimension.yaml +59 -0
- mapproxy/test/system/fixture/disable_storage.yaml +25 -0
- mapproxy/test/system/fixture/empty_ogrdata.geojson +1 -0
- mapproxy/test/system/fixture/formats.yaml +72 -0
- mapproxy/test/system/fixture/inspire.yaml +101 -0
- mapproxy/test/system/fixture/inspire_full.yaml +124 -0
- mapproxy/test/system/fixture/kml_layer.yaml +66 -0
- mapproxy/test/system/fixture/layer.yaml +260 -0
- mapproxy/test/system/fixture/layergroups.yaml +57 -0
- mapproxy/test/system/fixture/layergroups_root.yaml +106 -0
- mapproxy/test/system/fixture/legendgraphic.yaml +93 -0
- mapproxy/test/system/fixture/mapnik_source.yaml +66 -0
- mapproxy/test/system/fixture/mapproxy_export.yaml +12 -0
- mapproxy/test/system/fixture/mapserver.yaml +23 -0
- mapproxy/test/system/fixture/minimal_cgi.py +16 -0
- mapproxy/test/system/fixture/mixed_mode.yaml +49 -0
- mapproxy/test/system/fixture/multi_cache_layers.yaml +111 -0
- mapproxy/test/system/fixture/multiapp1.yaml +20 -0
- mapproxy/test/system/fixture/multiapp2.yaml +19 -0
- mapproxy/test/system/fixture/renderd_client.yaml +55 -0
- mapproxy/test/system/fixture/scalehints.yaml +70 -0
- mapproxy/test/system/fixture/seed.yaml +94 -0
- mapproxy/test/system/fixture/seed_mapproxy.yaml +39 -0
- mapproxy/test/system/fixture/seed_old.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts.yaml +12 -0
- mapproxy/test/system/fixture/seed_timeouts_mapproxy.yaml +27 -0
- mapproxy/test/system/fixture/seedonly.yaml +51 -0
- mapproxy/test/system/fixture/sld.yaml +35 -0
- mapproxy/test/system/fixture/source_errors.yaml +84 -0
- mapproxy/test/system/fixture/source_errors_raise.yaml +82 -0
- mapproxy/test/system/fixture/tileservice_origin.yaml +26 -0
- mapproxy/test/system/fixture/tileservice_refresh.yaml +59 -0
- mapproxy/test/system/fixture/tilesource_minmax_res.yaml +22 -0
- mapproxy/test/system/fixture/util-conf-base-grids.yaml +5 -0
- mapproxy/test/system/fixture/util-conf-overwrite.yaml +13 -0
- mapproxy/test/system/fixture/util-conf-wms-111-cap.xml +90 -0
- mapproxy/test/system/fixture/util_grids.yaml +29 -0
- mapproxy/test/system/fixture/util_wms_capabilities111.xml +130 -0
- mapproxy/test/system/fixture/util_wms_capabilities130.xml +100 -0
- mapproxy/test/system/fixture/util_wms_capabilities_service_exception.xml +5 -0
- mapproxy/test/system/fixture/watermark.yaml +50 -0
- mapproxy/test/system/fixture/wms_srs_extent.yaml +39 -0
- mapproxy/test/system/fixture/wms_versions.yaml +38 -0
- mapproxy/test/system/fixture/wmts.yaml +134 -0
- mapproxy/test/system/fixture/wmts_dimensions.yaml +57 -0
- mapproxy/test/system/fixture/xslt_featureinfo.yaml +54 -0
- mapproxy/test/system/fixture/xslt_featureinfo_input.yaml +51 -0
- mapproxy/test/system/test_arcgis.py +156 -0
- mapproxy/test/system/test_auth.py +1133 -0
- mapproxy/test/system/test_behind_proxy.py +75 -0
- mapproxy/test/system/test_bulk_meta_tiles.py +106 -0
- mapproxy/test/system/test_cache_azureblob.py +127 -0
- mapproxy/test/system/test_cache_band_merge.py +103 -0
- mapproxy/test/system/test_cache_coverage.py +168 -0
- mapproxy/test/system/test_cache_geopackage.py +144 -0
- mapproxy/test/system/test_cache_grid_names.py +89 -0
- mapproxy/test/system/test_cache_mbtiles.py +85 -0
- mapproxy/test/system/test_cache_s3.py +115 -0
- mapproxy/test/system/test_cache_source.py +146 -0
- mapproxy/test/system/test_combined_sources.py +335 -0
- mapproxy/test/system/test_coverage.py +140 -0
- mapproxy/test/system/test_decorate_img.py +214 -0
- mapproxy/test/system/test_demo.py +56 -0
- mapproxy/test/system/test_demo_with_extra_service.py +57 -0
- mapproxy/test/system/test_dimensions.py +279 -0
- mapproxy/test/system/test_disable_storage.py +42 -0
- mapproxy/test/system/test_formats.py +219 -0
- mapproxy/test/system/test_inspire_vs.py +173 -0
- mapproxy/test/system/test_kml.py +264 -0
- mapproxy/test/system/test_layergroups.py +160 -0
- mapproxy/test/system/test_legendgraphic.py +308 -0
- mapproxy/test/system/test_mapnik.py +162 -0
- mapproxy/test/system/test_mapserver.py +83 -0
- mapproxy/test/system/test_mixed_mode_format.py +201 -0
- mapproxy/test/system/test_multi_cache_layers.py +169 -0
- mapproxy/test/system/test_multiapp.py +92 -0
- mapproxy/test/system/test_refresh.py +206 -0
- mapproxy/test/system/test_renderd_client.py +304 -0
- mapproxy/test/system/test_response_headers.py +54 -0
- mapproxy/test/system/test_scalehints.py +140 -0
- mapproxy/test/system/test_seed.py +425 -0
- mapproxy/test/system/test_seed_only.py +93 -0
- mapproxy/test/system/test_sld.py +120 -0
- mapproxy/test/system/test_source_errors.py +377 -0
- mapproxy/test/system/test_tilesource_minmax_res.py +54 -0
- mapproxy/test/system/test_tms.py +277 -0
- mapproxy/test/system/test_tms_origin.py +46 -0
- mapproxy/test/system/test_util_conf.py +434 -0
- mapproxy/test/system/test_util_export.py +210 -0
- mapproxy/test/system/test_util_grids.py +88 -0
- mapproxy/test/system/test_util_wms_capabilities.py +182 -0
- mapproxy/test/system/test_watermark.py +91 -0
- mapproxy/test/system/test_wms.py +1616 -0
- mapproxy/test/system/test_wms_srs_extent.py +165 -0
- mapproxy/test/system/test_wms_version.py +85 -0
- mapproxy/test/system/test_wmsc.py +116 -0
- mapproxy/test/system/test_wmts.py +334 -0
- mapproxy/test/system/test_wmts_dimensions.py +206 -0
- mapproxy/test/system/test_wmts_restful.py +198 -0
- mapproxy/test/system/test_xslt_featureinfo.py +423 -0
- mapproxy/test/test_http_helper.py +217 -0
- mapproxy/test/unit/__init__.py +0 -0
- mapproxy/test/unit/epsg +2 -0
- mapproxy/test/unit/polygons/polygons.dbf +0 -0
- mapproxy/test/unit/polygons/polygons.shp +0 -0
- mapproxy/test/unit/polygons/polygons.shx +0 -0
- mapproxy/test/unit/test_async.py +242 -0
- mapproxy/test/unit/test_auth.py +430 -0
- mapproxy/test/unit/test_cache.py +1356 -0
- mapproxy/test/unit/test_cache_azureblob.py +97 -0
- mapproxy/test/unit/test_cache_compact.py +324 -0
- mapproxy/test/unit/test_cache_couchdb.py +118 -0
- mapproxy/test/unit/test_cache_geopackage.py +256 -0
- mapproxy/test/unit/test_cache_redis.py +123 -0
- mapproxy/test/unit/test_cache_riak.py +80 -0
- mapproxy/test/unit/test_cache_s3.py +93 -0
- mapproxy/test/unit/test_cache_tile.py +477 -0
- mapproxy/test/unit/test_client.py +488 -0
- mapproxy/test/unit/test_client_arcgis.py +74 -0
- mapproxy/test/unit/test_client_cgi.py +140 -0
- mapproxy/test/unit/test_collections.py +116 -0
- mapproxy/test/unit/test_concat_legends.py +37 -0
- mapproxy/test/unit/test_conf_loader.py +1267 -0
- mapproxy/test/unit/test_conf_validator.py +427 -0
- mapproxy/test/unit/test_config.py +118 -0
- mapproxy/test/unit/test_decorate_img.py +185 -0
- mapproxy/test/unit/test_exceptions.py +270 -0
- mapproxy/test/unit/test_featureinfo.py +313 -0
- mapproxy/test/unit/test_file_lock_load.py +49 -0
- mapproxy/test/unit/test_geom.py +512 -0
- mapproxy/test/unit/test_grid.py +1279 -0
- mapproxy/test/unit/test_image.py +1051 -0
- mapproxy/test/unit/test_image_mask.py +181 -0
- mapproxy/test/unit/test_image_messages.py +209 -0
- mapproxy/test/unit/test_image_options.py +160 -0
- mapproxy/test/unit/test_isodate.py +118 -0
- mapproxy/test/unit/test_multiapp.py +163 -0
- mapproxy/test/unit/test_ogr_reader.py +51 -0
- mapproxy/test/unit/test_request.py +745 -0
- mapproxy/test/unit/test_request_wmts.py +178 -0
- mapproxy/test/unit/test_response.py +78 -0
- mapproxy/test/unit/test_seed.py +365 -0
- mapproxy/test/unit/test_seed_cachelock.py +91 -0
- mapproxy/test/unit/test_srs.py +215 -0
- mapproxy/test/unit/test_tiled_source.py +122 -0
- mapproxy/test/unit/test_tilefilter.py +31 -0
- mapproxy/test/unit/test_times.py +25 -0
- mapproxy/test/unit/test_timeutils.py +50 -0
- mapproxy/test/unit/test_util_conf_utils.py +75 -0
- mapproxy/test/unit/test_utils.py +476 -0
- mapproxy/test/unit/test_wms_capabilities.py +44 -0
- mapproxy/test/unit/test_wms_layer.py +113 -0
- mapproxy/test/unit/test_yaml.py +68 -0
- mapproxy/tilefilter.py +61 -0
- mapproxy/util/__init__.py +0 -0
- mapproxy/util/async_.py +229 -0
- mapproxy/util/collections.py +134 -0
- mapproxy/util/coverage.py +337 -0
- mapproxy/util/ext/__init__.py +14 -0
- mapproxy/util/ext/dictspec/__init__.py +1 -0
- mapproxy/util/ext/dictspec/spec.py +131 -0
- mapproxy/util/ext/dictspec/test/__init__.py +0 -0
- mapproxy/util/ext/dictspec/test/test_validator.py +278 -0
- mapproxy/util/ext/dictspec/validator.py +194 -0
- mapproxy/util/ext/local.py +198 -0
- mapproxy/util/ext/lockfile.py +140 -0
- mapproxy/util/ext/odict.py +321 -0
- mapproxy/util/ext/serving.py +491 -0
- mapproxy/util/ext/tempita/__init__.py +1093 -0
- mapproxy/util/ext/tempita/_looper.py +163 -0
- mapproxy/util/ext/tempita/string_utils.py +24 -0
- mapproxy/util/ext/wmsparse/__init__.py +3 -0
- mapproxy/util/ext/wmsparse/duration.py +600 -0
- mapproxy/util/ext/wmsparse/parse.py +307 -0
- mapproxy/util/ext/wmsparse/test/__init__.py +0 -0
- mapproxy/util/ext/wmsparse/test/test_parse.py +111 -0
- mapproxy/util/ext/wmsparse/test/test_util.py +23 -0
- mapproxy/util/ext/wmsparse/test/wms-example-111.xml +90 -0
- mapproxy/util/ext/wmsparse/test/wms-example-130.xml +120 -0
- mapproxy/util/ext/wmsparse/test/wms-large-111.xml +2114 -0
- mapproxy/util/ext/wmsparse/test/wms_nasa_cap.xml +386 -0
- mapproxy/util/ext/wmsparse/util.py +189 -0
- mapproxy/util/fs.py +164 -0
- mapproxy/util/geom.py +307 -0
- mapproxy/util/lib.py +117 -0
- mapproxy/util/lock.py +171 -0
- mapproxy/util/ogr.py +247 -0
- mapproxy/util/py.py +75 -0
- mapproxy/util/times.py +78 -0
- mapproxy/util/yaml.py +58 -0
- mapproxy/version.py +33 -0
- mapproxy/wsgiapp.py +167 -0
|
File without changes
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2010 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
from mapproxy.client.http import HTTPClient
|
|
17
|
+
from mapproxy.client.wms import WMSInfoClient
|
|
18
|
+
from mapproxy.srs import SRS
|
|
19
|
+
from mapproxy.featureinfo import create_featureinfo_doc
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ArcGISClient(object):
|
|
23
|
+
def __init__(self, request_template, http_client=None):
|
|
24
|
+
self.request_template = request_template
|
|
25
|
+
self.http_client = http_client
|
|
26
|
+
|
|
27
|
+
def retrieve(self, query, format):
|
|
28
|
+
url = self._query_url(query, format)
|
|
29
|
+
resp = self.http_client.open(url)
|
|
30
|
+
return resp
|
|
31
|
+
|
|
32
|
+
def _query_url(self, query, format):
|
|
33
|
+
req = self.request_template.copy()
|
|
34
|
+
req.params.format = format
|
|
35
|
+
req.params.bbox = query.bbox
|
|
36
|
+
req.params.size = query.size
|
|
37
|
+
req.params.bboxSR = query.srs
|
|
38
|
+
req.params.imageSR = query.srs
|
|
39
|
+
req.params.transparent = query.transparent
|
|
40
|
+
|
|
41
|
+
return req.complete_url
|
|
42
|
+
|
|
43
|
+
def combined_client(self, other, query):
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ArcGISInfoClient(WMSInfoClient):
|
|
48
|
+
def __init__(self, request_template, supported_srs=None, http_client=None,
|
|
49
|
+
return_geometries=False,
|
|
50
|
+
tolerance=5,
|
|
51
|
+
):
|
|
52
|
+
self.request_template = request_template
|
|
53
|
+
self.http_client = http_client or HTTPClient()
|
|
54
|
+
if not supported_srs and self.request_template.params.srs is not None:
|
|
55
|
+
supported_srs = [SRS(self.request_template.params.srs)]
|
|
56
|
+
self.supported_srs = supported_srs or []
|
|
57
|
+
self.return_geometries = return_geometries
|
|
58
|
+
self.tolerance = tolerance
|
|
59
|
+
|
|
60
|
+
def get_info(self, query):
|
|
61
|
+
if self.supported_srs and query.srs not in self.supported_srs:
|
|
62
|
+
query = self._get_transformed_query(query)
|
|
63
|
+
resp = self._retrieve(query)
|
|
64
|
+
# always use query.info_format and not content-type from response (even esri example server aleays return
|
|
65
|
+
# text/plain)
|
|
66
|
+
return create_featureinfo_doc(resp.read(), query.info_format)
|
|
67
|
+
|
|
68
|
+
def _query_url(self, query):
|
|
69
|
+
req = self.request_template.copy()
|
|
70
|
+
req.params.bbox = query.bbox
|
|
71
|
+
req.params.size = query.size
|
|
72
|
+
req.params.pos = query.pos
|
|
73
|
+
req.params.srs = query.srs.srs_code
|
|
74
|
+
if query.info_format.startswith('text/html'):
|
|
75
|
+
req.params['f'] = 'html'
|
|
76
|
+
else:
|
|
77
|
+
req.params['f'] = 'json'
|
|
78
|
+
|
|
79
|
+
req.params['tolerance'] = self.tolerance
|
|
80
|
+
req.params['returnGeometry'] = str(self.return_geometries).lower()
|
|
81
|
+
|
|
82
|
+
return req.complete_url
|
mapproxy/client/cgi.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2011 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
HTTP client that directly calls CGI executable.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import errno
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import subprocess
|
|
24
|
+
import time
|
|
25
|
+
from io import BytesIO
|
|
26
|
+
from urllib.parse import urlparse
|
|
27
|
+
|
|
28
|
+
from mapproxy.source import SourceError
|
|
29
|
+
from mapproxy.image import ImageSource
|
|
30
|
+
from mapproxy.client.http import HTTPClientError
|
|
31
|
+
from mapproxy.client.log import log_request
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def split_cgi_response(data):
|
|
35
|
+
headers = []
|
|
36
|
+
prev_n = 0
|
|
37
|
+
while True:
|
|
38
|
+
next_n = data.find(b'\n', prev_n)
|
|
39
|
+
if next_n < 0:
|
|
40
|
+
break
|
|
41
|
+
next_line_begin = data[next_n+1:next_n+3]
|
|
42
|
+
headers.append(data[prev_n:next_n].rstrip(b'\r'))
|
|
43
|
+
if next_line_begin[0:1] == b'\n':
|
|
44
|
+
return headers_dict(headers), data[next_n+2:]
|
|
45
|
+
elif next_line_begin == b'\r\n':
|
|
46
|
+
return headers_dict(headers), data[next_n+3:]
|
|
47
|
+
prev_n = next_n+1
|
|
48
|
+
return {}, data
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def headers_dict(header_lines):
|
|
52
|
+
headers = {}
|
|
53
|
+
for line in header_lines:
|
|
54
|
+
if b':' in line:
|
|
55
|
+
key, value = line.split(b':', 1)
|
|
56
|
+
value = value.strip()
|
|
57
|
+
else:
|
|
58
|
+
key = line
|
|
59
|
+
value = None
|
|
60
|
+
key = key.decode('latin-1')
|
|
61
|
+
key = key[0].upper() + key[1:].lower()
|
|
62
|
+
if value:
|
|
63
|
+
value = value.decode('latin-1')
|
|
64
|
+
headers[key] = value
|
|
65
|
+
return headers
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class IOwithHeaders(object):
|
|
69
|
+
def __init__(self, io, headers):
|
|
70
|
+
self.io = io
|
|
71
|
+
self.headers = headers
|
|
72
|
+
|
|
73
|
+
def __getattr__(self, name):
|
|
74
|
+
return getattr(self.io, name)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class CGIClient(object):
|
|
78
|
+
def __init__(self, script, no_headers=False, working_directory=None):
|
|
79
|
+
self.script = script
|
|
80
|
+
self.working_directory = working_directory
|
|
81
|
+
self.no_headers = no_headers
|
|
82
|
+
|
|
83
|
+
def open(self, url, data=None):
|
|
84
|
+
assert data is None, 'POST requests not supported by CGIClient'
|
|
85
|
+
|
|
86
|
+
parsed_url = urlparse(url)
|
|
87
|
+
environ = os.environ.copy()
|
|
88
|
+
environ.update({
|
|
89
|
+
'QUERY_STRING': parsed_url.query,
|
|
90
|
+
'REQUEST_METHOD': 'GET',
|
|
91
|
+
'GATEWAY_INTERFACE': 'CGI/1.1',
|
|
92
|
+
'SERVER_ADDR': '127.0.0.1',
|
|
93
|
+
'SERVER_NAME': 'localhost',
|
|
94
|
+
'SERVER_PROTOCOL': 'HTTP/1.0',
|
|
95
|
+
'SERVER_SOFTWARE': 'MapProxy',
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
start_time = time.time()
|
|
99
|
+
try:
|
|
100
|
+
p = subprocess.Popen([self.script], env=environ,
|
|
101
|
+
stdout=subprocess.PIPE,
|
|
102
|
+
cwd=self.working_directory or os.path.dirname(self.script)
|
|
103
|
+
)
|
|
104
|
+
except OSError as ex:
|
|
105
|
+
if ex.errno == errno.ENOENT:
|
|
106
|
+
raise SourceError('CGI script not found (%s)' % (self.script,))
|
|
107
|
+
elif ex.errno == errno.EACCES:
|
|
108
|
+
raise SourceError('No permission for CGI script (%s)' % (self.script,))
|
|
109
|
+
else:
|
|
110
|
+
raise
|
|
111
|
+
|
|
112
|
+
stdout = p.communicate()[0]
|
|
113
|
+
ret = p.wait()
|
|
114
|
+
if ret != 0:
|
|
115
|
+
raise HTTPClientError('Error during CGI call (exit code: %d)'
|
|
116
|
+
% (ret, ))
|
|
117
|
+
|
|
118
|
+
if self.no_headers:
|
|
119
|
+
content = stdout
|
|
120
|
+
headers = dict()
|
|
121
|
+
else:
|
|
122
|
+
headers, content = split_cgi_response(stdout)
|
|
123
|
+
|
|
124
|
+
status_match = re.match(r'(\d\d\d) ', headers.get('Status', ''))
|
|
125
|
+
if status_match:
|
|
126
|
+
status_code = status_match.group(1)
|
|
127
|
+
else:
|
|
128
|
+
status_code = '-'
|
|
129
|
+
size = len(content)
|
|
130
|
+
content = IOwithHeaders(BytesIO(content), headers)
|
|
131
|
+
|
|
132
|
+
log_request('%s:%s' % (self.script, parsed_url.query),
|
|
133
|
+
status_code, size=size, method='CGI', duration=time.time()-start_time)
|
|
134
|
+
return content
|
|
135
|
+
|
|
136
|
+
def open_image(self, url, data=None):
|
|
137
|
+
resp = self.open(url, data=data)
|
|
138
|
+
if 'Content-type' in resp.headers:
|
|
139
|
+
if not resp.headers['Content-type'].lower().startswith('image'):
|
|
140
|
+
raise HTTPClientError('response is not an image: (%s)' % (resp.read()))
|
|
141
|
+
return ImageSource(resp)
|
mapproxy/client/http.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2010-2017 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
Tile retrieval (WMS, TMS, etc.).
|
|
18
|
+
"""
|
|
19
|
+
import sys
|
|
20
|
+
import time
|
|
21
|
+
|
|
22
|
+
from mapproxy.version import version
|
|
23
|
+
from mapproxy.image import ImageSource
|
|
24
|
+
from mapproxy.util.py import reraise_exception
|
|
25
|
+
from mapproxy.client.log import log_request
|
|
26
|
+
|
|
27
|
+
from urllib import request as urllib2
|
|
28
|
+
from urllib.error import URLError, HTTPError
|
|
29
|
+
from urllib.request import HTTPCookieProcessor
|
|
30
|
+
from http import client as httplib
|
|
31
|
+
from http.cookiejar import CookieJar
|
|
32
|
+
|
|
33
|
+
import socket
|
|
34
|
+
import ssl
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class HTTPClientError(Exception):
|
|
38
|
+
def __init__(self, arg, response_code=None, full_msg=None):
|
|
39
|
+
Exception.__init__(self, arg)
|
|
40
|
+
self.response_code = response_code
|
|
41
|
+
self.full_msg = full_msg
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_https_handler(ssl_ca_certs, insecure):
|
|
45
|
+
if insecure:
|
|
46
|
+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
47
|
+
ctx.check_hostname = False
|
|
48
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
49
|
+
elif ssl_ca_certs:
|
|
50
|
+
ctx = ssl.create_default_context(cafile=ssl_ca_certs)
|
|
51
|
+
else:
|
|
52
|
+
ctx = ssl.create_default_context()
|
|
53
|
+
return urllib2.HTTPSHandler(context=ctx)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class VerifiedHTTPSConnection(httplib.HTTPSConnection):
|
|
57
|
+
def __init__(self, *args, **kw):
|
|
58
|
+
self._ca_certs = kw.pop('ca_certs', None)
|
|
59
|
+
httplib.HTTPSConnection.__init__(self, *args, **kw)
|
|
60
|
+
|
|
61
|
+
def connect(self):
|
|
62
|
+
# overrides the version in httplib so that we do
|
|
63
|
+
# certificate verification
|
|
64
|
+
|
|
65
|
+
if hasattr(socket, 'create_connection') and hasattr(self, 'source_address'):
|
|
66
|
+
sock = socket.create_connection((self.host, self.port),
|
|
67
|
+
self.timeout, self.source_address)
|
|
68
|
+
else:
|
|
69
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
70
|
+
sock.connect((self.host, self.port))
|
|
71
|
+
|
|
72
|
+
if hasattr(self, '_tunnel_host') and self._tunnel_host:
|
|
73
|
+
# for Python >= 2.6 with proxy support
|
|
74
|
+
self.sock = sock
|
|
75
|
+
self._tunnel()
|
|
76
|
+
|
|
77
|
+
# wrap the socket using verification with the root
|
|
78
|
+
# certs in self.ca_certs_path
|
|
79
|
+
self.sock = ssl.wrap_socket(sock,
|
|
80
|
+
self.key_file,
|
|
81
|
+
self.cert_file,
|
|
82
|
+
cert_reqs=ssl.CERT_REQUIRED,
|
|
83
|
+
ca_certs=self._ca_certs)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def verified_https_connection_with_ca_certs(ca_certs):
|
|
87
|
+
"""
|
|
88
|
+
Creates VerifiedHTTPSConnection classes with given ca_certs file.
|
|
89
|
+
"""
|
|
90
|
+
def wrapper(*args, **kw):
|
|
91
|
+
kw['ca_certs'] = ca_certs
|
|
92
|
+
return VerifiedHTTPSConnection(*args, **kw)
|
|
93
|
+
return wrapper
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class VerifiedHTTPSHandler(urllib2.HTTPSHandler):
|
|
97
|
+
def __init__(self, connection_class=VerifiedHTTPSConnection):
|
|
98
|
+
self.specialized_conn_class = connection_class
|
|
99
|
+
urllib2.HTTPSHandler.__init__(self)
|
|
100
|
+
|
|
101
|
+
def https_open(self, req):
|
|
102
|
+
return self.do_open(self.specialized_conn_class, req)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class _URLOpenerCache(object):
|
|
106
|
+
"""
|
|
107
|
+
Creates custom URLOpener with BasicAuth and HTTPS handler.
|
|
108
|
+
|
|
109
|
+
Caches and reuses opener if possible (i.e. if they share the same
|
|
110
|
+
ssl_ca_certs).
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self):
|
|
114
|
+
self._opener = {}
|
|
115
|
+
|
|
116
|
+
def __call__(self, ssl_ca_certs, url, username, password, insecure=False, manage_cookies=False):
|
|
117
|
+
cache_key = (ssl_ca_certs, insecure, manage_cookies)
|
|
118
|
+
if cache_key not in self._opener:
|
|
119
|
+
handlers = []
|
|
120
|
+
https_handler = build_https_handler(ssl_ca_certs, insecure)
|
|
121
|
+
if https_handler:
|
|
122
|
+
handlers.append(https_handler)
|
|
123
|
+
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
|
124
|
+
authhandler = urllib2.HTTPBasicAuthHandler(passman)
|
|
125
|
+
handlers.append(authhandler)
|
|
126
|
+
authhandler = urllib2.HTTPDigestAuthHandler(passman)
|
|
127
|
+
handlers.append(authhandler)
|
|
128
|
+
if manage_cookies:
|
|
129
|
+
cj = CookieJar()
|
|
130
|
+
handlers.append(HTTPCookieProcessor(cj))
|
|
131
|
+
|
|
132
|
+
opener = urllib2.build_opener(*handlers)
|
|
133
|
+
|
|
134
|
+
opener.addheaders = [('User-agent', 'MapProxy-%s' % (version,))]
|
|
135
|
+
|
|
136
|
+
self._opener[cache_key] = (opener, passman)
|
|
137
|
+
else:
|
|
138
|
+
opener, passman = self._opener[cache_key]
|
|
139
|
+
|
|
140
|
+
if url is not None and username is not None and password is not None:
|
|
141
|
+
passman.add_password(None, url, username, password)
|
|
142
|
+
|
|
143
|
+
return opener
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
create_url_opener = _URLOpenerCache()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class HTTPClient(object):
|
|
150
|
+
def __init__(self, url=None, username=None, password=None, insecure=False,
|
|
151
|
+
ssl_ca_certs=None, timeout=None, headers=None, hide_error_details=False,
|
|
152
|
+
manage_cookies=False):
|
|
153
|
+
self._timeout = timeout
|
|
154
|
+
if url and url.startswith('https'):
|
|
155
|
+
if insecure:
|
|
156
|
+
ssl_ca_certs = None
|
|
157
|
+
|
|
158
|
+
self.opener = create_url_opener(ssl_ca_certs, url, username, password,
|
|
159
|
+
insecure=insecure, manage_cookies=manage_cookies)
|
|
160
|
+
self.header_list = headers.items() if headers else []
|
|
161
|
+
self.hide_error_details = hide_error_details
|
|
162
|
+
|
|
163
|
+
def open(self, url, data=None, method=None):
|
|
164
|
+
code = None
|
|
165
|
+
result = None
|
|
166
|
+
try:
|
|
167
|
+
req = urllib2.Request(url, data=data)
|
|
168
|
+
except ValueError as e:
|
|
169
|
+
err = self.handle_url_exception(url, 'URL not correct', e.args[0])
|
|
170
|
+
reraise_exception(err, sys.exc_info())
|
|
171
|
+
for key, value in self.header_list:
|
|
172
|
+
req.add_header(key, value)
|
|
173
|
+
if method:
|
|
174
|
+
req.method = method
|
|
175
|
+
try:
|
|
176
|
+
start_time = time.time()
|
|
177
|
+
if self._timeout is not None:
|
|
178
|
+
result = self.opener.open(req, timeout=self._timeout)
|
|
179
|
+
else:
|
|
180
|
+
result = self.opener.open(req)
|
|
181
|
+
except HTTPError as e:
|
|
182
|
+
code = e.code
|
|
183
|
+
err = self.handle_url_exception(url, 'HTTP Error', str(code), response_code=code)
|
|
184
|
+
reraise_exception(err, sys.exc_info())
|
|
185
|
+
except URLError as e:
|
|
186
|
+
if isinstance(e.reason, ssl.SSLError):
|
|
187
|
+
err = self.handle_url_exception(url, 'Could not verify connection to URL', e.reason.args[1])
|
|
188
|
+
reraise_exception(err, sys.exc_info())
|
|
189
|
+
try:
|
|
190
|
+
reason = e.reason.args[1]
|
|
191
|
+
except (AttributeError, IndexError):
|
|
192
|
+
reason = e.reason
|
|
193
|
+
err = self.handle_url_exception(url, 'No response from URL', reason)
|
|
194
|
+
reraise_exception(err, sys.exc_info())
|
|
195
|
+
except ValueError as e:
|
|
196
|
+
err = self.handle_url_exception(url, 'URL not correct', e.args[0])
|
|
197
|
+
reraise_exception(err, sys.exc_info())
|
|
198
|
+
except Exception as e:
|
|
199
|
+
err = self.handle_url_exception(url, 'Internal HTTP error', repr(e))
|
|
200
|
+
reraise_exception(err, sys.exc_info())
|
|
201
|
+
else:
|
|
202
|
+
code = getattr(result, 'code', 200)
|
|
203
|
+
if code == 204:
|
|
204
|
+
raise HTTPClientError('HTTP Error "204 No Content"', response_code=204)
|
|
205
|
+
return result
|
|
206
|
+
finally:
|
|
207
|
+
log_request(url, code, result, duration=time.time()-start_time, method=req.get_method())
|
|
208
|
+
|
|
209
|
+
def open_image(self, url, data=None):
|
|
210
|
+
resp = self.open(url, data=data)
|
|
211
|
+
if 'content-type' in resp.headers:
|
|
212
|
+
if not resp.headers['content-type'].lower().startswith('image'):
|
|
213
|
+
raise HTTPClientError('response is not an image: (%s)' % (resp.read()))
|
|
214
|
+
return ImageSource(resp)
|
|
215
|
+
|
|
216
|
+
def handle_url_exception(self, url, message, reason, response_code=None):
|
|
217
|
+
full_msg = '%s "%s": %s' % (message, url, reason)
|
|
218
|
+
if self.hide_error_details:
|
|
219
|
+
return HTTPClientError(
|
|
220
|
+
'{} (see logs for URL and reason).'.format(message),
|
|
221
|
+
response_code=response_code,
|
|
222
|
+
full_msg=full_msg,
|
|
223
|
+
)
|
|
224
|
+
else:
|
|
225
|
+
return HTTPClientError(
|
|
226
|
+
full_msg,
|
|
227
|
+
response_code=response_code,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def auth_data_from_url(url):
|
|
232
|
+
"""
|
|
233
|
+
>>> auth_data_from_url('invalid_url')
|
|
234
|
+
('invalid_url', (None, None))
|
|
235
|
+
>>> auth_data_from_url('http://localhost/bar')
|
|
236
|
+
('http://localhost/bar', (None, None))
|
|
237
|
+
>>> auth_data_from_url('http://bar@localhost/bar')
|
|
238
|
+
('http://localhost/bar', ('bar', None))
|
|
239
|
+
>>> auth_data_from_url('http://bar:baz@localhost/bar')
|
|
240
|
+
('http://localhost/bar', ('bar', 'baz'))
|
|
241
|
+
>>> auth_data_from_url('http://bar:b:az@@localhost/bar')
|
|
242
|
+
('http://localhost/bar', ('bar', 'b:az@'))
|
|
243
|
+
>>> auth_data_from_url('http://bar foo; foo@bar:b:az@@localhost/bar')
|
|
244
|
+
('http://localhost/bar', ('bar foo; foo@bar', 'b:az@'))
|
|
245
|
+
>>> auth_data_from_url('https://bar:foo#;%$@localhost/bar')
|
|
246
|
+
('https://localhost/bar', ('bar', 'foo#;%$'))
|
|
247
|
+
>>> auth_data_from_url('http://localhost/bar@2x')
|
|
248
|
+
('http://localhost/bar@2x', (None, None))
|
|
249
|
+
>>> auth_data_from_url('http://bar@localhost/bar@2x')
|
|
250
|
+
('http://localhost/bar@2x', ('bar', None))
|
|
251
|
+
>>> auth_data_from_url('http://bar:baz@localhost/bar@2x')
|
|
252
|
+
('http://localhost/bar@2x', ('bar', 'baz'))
|
|
253
|
+
>>> auth_data_from_url('https://bar@localhost/bar/0/0/0@2x.png')
|
|
254
|
+
('https://localhost/bar/0/0/0@2x.png', ('bar', None))
|
|
255
|
+
>>> auth_data_from_url('http://bar:baz@localhost/bar@2x.png')
|
|
256
|
+
('http://localhost/bar@2x.png', ('bar', 'baz'))
|
|
257
|
+
"""
|
|
258
|
+
if not url or '://' not in url:
|
|
259
|
+
# be silent for invalid URLs
|
|
260
|
+
return url, (None, None)
|
|
261
|
+
|
|
262
|
+
schema, url = url.split('://', 1)
|
|
263
|
+
if '/' in url:
|
|
264
|
+
host, request = url.split('/', 1)
|
|
265
|
+
else:
|
|
266
|
+
host, request = url, ''
|
|
267
|
+
|
|
268
|
+
username = password = None
|
|
269
|
+
if '@' in host:
|
|
270
|
+
auth_data, host = host.rsplit('@', 1)
|
|
271
|
+
if ':' in auth_data:
|
|
272
|
+
username, password = auth_data.split(':', 1)
|
|
273
|
+
else:
|
|
274
|
+
username = auth_data
|
|
275
|
+
url = schema + "://" + host + "/" + request
|
|
276
|
+
return url, (username, password)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def open_url(url):
|
|
280
|
+
url, (username, password) = auth_data_from_url(url)
|
|
281
|
+
http_client = HTTPClient(url, username, password)
|
|
282
|
+
return http_client.open(url)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def retrieve_image(url, client=None):
|
|
286
|
+
"""
|
|
287
|
+
Retrive an image from `url`.
|
|
288
|
+
|
|
289
|
+
:return: the image as a file object (with url .header and .info)
|
|
290
|
+
:raise HTTPClientError: if response content-type doesn't start with image
|
|
291
|
+
"""
|
|
292
|
+
resp = open_url(url)
|
|
293
|
+
if not resp.headers['content-type'].startswith('image'):
|
|
294
|
+
raise HTTPClientError('response is not an image: (%s)' % (resp.read()))
|
|
295
|
+
return ImageSource(resp)
|
mapproxy/client/log.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This file is part of the MapProxy project.
|
|
2
|
+
# Copyright (C) 2011 Omniscale <http://omniscale.de>
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
logger = logging.getLogger('mapproxy.source.request')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def log_request(url, status, result=None, size=None, method='GET', duration=None):
|
|
21
|
+
if not logger.isEnabledFor(logging.INFO):
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
if not size and result is not None:
|
|
25
|
+
size = result.headers.get('Content-length')
|
|
26
|
+
if size:
|
|
27
|
+
size = '%.1f' % (int(size)/1024.0, )
|
|
28
|
+
else:
|
|
29
|
+
size = '-'
|
|
30
|
+
if not status:
|
|
31
|
+
status = '-'
|
|
32
|
+
duration = '%d' % (duration*1000) if duration else '-'
|
|
33
|
+
logger.info('%s %s %s %s %s', method, url.replace(' ', ''), status, size, duration)
|