def do_GET_body(self): """Create body of GET.""" iiif = self.iiif if len(self.path) > 1024: raise IIIFError(code=414, text="URI Too Long: Max 1024 chars, got %d\n" % len(self.path)) try: # self.path has leading / then identifier/params... self.path = self.path.lstrip("/") sys.stderr.write("path = %s" % (self.path)) iiif.parse_url(self.path) except Exception as e: # Something completely unexpected => 500 raise IIIFError( code=500, text="Internal Server Error: unexpected exception parsing request (" + str(e) + ")" ) # Now we have a full iiif request if re.match("[\w\.\-]+$", iiif.identifier): file = os.path.join(TESTIMAGE_DIR, iiif.identifier) if not os.path.isfile(file): images_available = "" for image_file in os.listdir(TESTIMAGE_DIR): if os.path.isfile(os.path.join(TESTIMAGE_DIR, image_file)): images_available += " " + image_file + "\n" raise IIIFError( code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Local image files available:\n" + images_available, ) else: raise IIIFError( code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Only local test images and http: URIs for images are supported.\n", ) # Now know image is OK manipulator = IIIFRequestHandler.manipulator_class() # Stash manipulator object so we can cleanup after reading file self.manipulator = manipulator self.compliance_uri = manipulator.compliance_uri if iiif.info: # get size manipulator.srcfile = file manipulator.do_first() # most of info.json comes from config, a few things # specific to image i = IIIFInfo() i.identifier = self.iiif.identifier i.width = manipulator.width i.height = manipulator.height import io return (io.StringIO(i.as_json()), "application/json") else: (outfile, mime_type) = manipulator.derive(file, iiif) return (open(outfile, "r"), mime_type)
def test01_minmal(self): # Just do the trivial JSON test ir = IIIFInfo(server_and_prefix="http://example.com",identifier="i1",api_version='1.1') self.assertEqual( ir.as_json(validate=False), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", \n "width": 100\n}' )
def do_GET_body(self): """Create body of GET.""" iiif = self.iiif if (len(self.path) > 1024): raise IIIFError(code=414, text="URI Too Long: Max 1024 chars, got %d\n" % len(self.path)) try: # self.path has leading / then identifier/params... self.path = self.path.lstrip('/') sys.stderr.write("path = %s" % (self.path)) iiif.parse_url(self.path) except Exception as e: # Something completely unexpected => 500 raise IIIFError( code=500, text= "Internal Server Error: unexpected exception parsing request (" + str(e) + ")") # Now we have a full iiif request if (re.match('[\w\.\-]+$', iiif.identifier)): file = os.path.join(TESTIMAGE_DIR, iiif.identifier) if (not os.path.isfile(file)): images_available = "" for image_file in os.listdir(TESTIMAGE_DIR): if (os.path.isfile(os.path.join(TESTIMAGE_DIR, image_file))): images_available += " " + image_file + "\n" raise IIIFError(code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Local image files available:\n" + images_available) else: raise IIIFError( code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Only local test images and http: URIs for images are supported.\n" ) # Now know image is OK manipulator = IIIFRequestHandler.manipulator_class() # Stash manipulator object so we can cleanup after reading file self.manipulator = manipulator self.compliance_uri = manipulator.compliance_uri if (iiif.info): # get size manipulator.srcfile = file manipulator.do_first() # most of info.json comes from config, a few things # specific to image i = IIIFInfo() i.identifier = self.iiif.identifier i.width = manipulator.width i.height = manipulator.height import io return (io.StringIO(i.as_json()), "application/json") else: (outfile, mime_type) = manipulator.derive(file, iiif) return (open(outfile, 'r'), mime_type)
def test01_minmal(self): # Just do the trivial JSON test ir = IIIFInfo(identifier="i1",api_version='1.0') self.assertEqual( ir.as_json(validate=False), '{\n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "height": 200, \n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1", \n "width": 100\n}' )
def image_information_response(self): """Parse image information request and create response.""" dr = degraded_request(self.identifier) if (dr): self.logger.info("image_information: degraded %s -> %s" % (self.identifier,dr)) self.degraded = self.identifier self.identifier = dr else: self.logger.info("image_information: %s" % (self.identifier)) # get size self.manipulator.srcfile=self.file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image info = { 'tile_height': self.config.tile_height, 'tile_width': self.config.tile_width, 'scale_factors' : self.config.scale_factors } i = IIIFInfo(conf=info,api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height if (self.api_version>='2.0'): i.qualities = [ "default", "color", "gray" ] #FIXME - should come from manipulator else: i.qualities = [ "native", "color", "gray" ] #FIXME - should come from manipulator i.formats = [ "jpg", "png" ] #FIXME - should come from manipulator if (self.auth): self.auth.add_services(i) return self.make_response(i.as_json(),content_type=self.json_mime_type)
def test01_minmal(self): # Just do the trivial JSON test # ?? should this empty case raise and error instead? ir = IIIFInfo(identifier="http://example.com/i1") self.assertEqual( ir.as_json(validate=False), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image", \n "width": 100\n}' )
def test01_minmal(self): # Just do the trivial JSON test # ?? should this empty case raise and error instead? ir = IIIFInfo(identifier="http://example.com/i1", api_version='2.1') self.assertEqual( ir.as_json(validate=False), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image", \n "width": 100\n}' )
def test01_minmal(self): """Trivial JSON test.""" ir = IIIFInfo(identifier="i1", api_version='1.0') self.assertJSONEqual(ir.as_json( validate=False), '{\n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1"\n}') ir.width = 100 ir.height = 200 self.assertJSONEqual(ir.as_json( ), '{\n "height": 200, \n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1", \n "width": 100\n}')
def test01_minmal(self): """Trivial JSON test.""" ir = IIIFInfo(server_and_prefix="http://example.com", identifier="i1", api_version='1.1') self.assertJSONEqual(ir.as_json( validate=False), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"\n}') ir.width = 100 ir.height = 200 self.assertJSONEqual(ir.as_json( ), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", \n "width": 100\n}')
def test01_minmal(self): """Trivial JSON test.""" # ?? should this empty case raise and error instead? ir = IIIFInfo(identifier="https://example.com/i1", api_version='3.0') self.assertJSONEqual( ir.as_json(validate=False), '{\n "@context": "http://iiif.io/api/image/3/context.json", \n ' '"id": "https://example.com/i1", \n "profile": "level1", \n ' '"protocol": "http://iiif.io/api/image", "type": "ImageService3"\n}' ) ir.width = 100 ir.height = 200 self.assertJSONEqual( ir.as_json(), '{\n "@context": "http://iiif.io/api/image/3/context.json", \n ' '"height": 200, \n "id": "https://example.com/i1", \n ' '"profile": "level1", \n "protocol": "http://iiif.io/api/image", \n' '"type": "ImageService3",\n "width": 100\n}')
def image_information_response(self): """Parse image information request and create response.""" dr = degraded_request(self.identifier) if (dr): self.logger.info("image_information: degraded %s -> %s" % (self.identifier, dr)) self.degraded = self.identifier self.identifier = dr else: self.logger.info("image_information: %s" % (self.identifier)) # get size self.manipulator.srcfile = self.file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image info = { 'tile_height': self.config.tile_height, 'tile_width': self.config.tile_width, 'scale_factors': self.config.scale_factors } # calculate scale factors if not hard-coded if ('auto' in self.config.scale_factors): info['scale_factors'] = self.manipulator.scale_factors( self.config.tile_width, self.config.tile_height) i = IIIFInfo(conf=info, api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height if (self.api_version >= '2.0'): # FIXME - should come from manipulator i.qualities = ["default", "color", "gray"] else: # FIXME - should come from manipulator i.qualities = ["native", "color", "gray"] i.formats = ["jpg", "png"] # FIXME - should come from manipulator if (self.auth): self.auth.add_services(i) return self.make_response( i.as_json(), headers={"Content-Type": self.json_mime_type})
def image_information_response(self): """Parse image information request and create response.""" dr = degraded_request(self.identifier) if dr: self.logger.info("image_information: degraded %s -> %s" % (self.identifier, dr)) self.degraded = self.identifier self.identifier = dr else: self.logger.info("image_information: %s" % (self.identifier)) # get size self.manipulator.srcfile = self.file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image info = { "tile_height": self.config.tile_height, "tile_width": self.config.tile_width, "scale_factors": self.config.scale_factors, } # calculate scale factors if not hard-coded if "auto" in self.config.scale_factors: info["scale_factors"] = self.manipulator.scale_factors(self.config.tile_width, self.config.tile_height) i = IIIFInfo(conf=info, api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height if self.api_version >= "2.0": # FIXME - should come from manipulator i.qualities = ["default", "color", "gray"] else: # FIXME - should come from manipulator i.qualities = ["native", "color", "gray"] i.formats = ["jpg", "png"] # FIXME - should come from manipulator if self.auth: self.auth.add_services(i) return self.make_response(i.as_json(), headers={"Content-Type": self.json_mime_type})
def do_GET_body(self): iiif=self.iiif if (len(self.path)>1024): raise IIIFError(code=414, text="URI Too Long: Max 1024 chars, got %d\n" % len(self.path)) #print "GET " + self.path try: iiif.parse_url(self.path) except IIIFRequestBaseURI as e: info_uri = self.server_and_prefix + '/' + urllib.quote(self.iiif.identifier) + '/info.json' raise IIIFError(code=303, headers={'Location': info_uri}) except IIIFError as e: # Pass through raise e except Exception as e: # Something completely unexpected => 500 raise IIIFError(code=500, text="Internal Server Error: unexpected exception parsing request (" + str(e) + ")") # URL path parsed OK, now determine how to handle request if (re.match('[\w\.\-]+$',iiif.identifier)): file = os.path.join(IIIFRequestHandler.IMAGE_DIR,iiif.identifier) if (not os.path.isfile(file)): images_available="" for image_file in os.listdir(IIIFRequestHandler.IMAGE_DIR): if (os.path.isfile(os.path.join(IIIFRequestHandler.IMAGE_DIR,image_file))): images_available += " "+image_file+"\n" raise IIIFError(code=404,parameter="identifier", text="Image resource '"+iiif.identifier+"' not found. Local image files available:\n" + images_available) else: raise IIIFError(code=404,parameter="identifier", text="Image resource '"+iiif.identifier+"' not found. Only local test images and http: URIs for images are supported.\n") # self.compliance_level=self.manipulator.complianceLevel # Do we have auth? if (self.auth_type == 'basic'): auth_map = parse_authorization_header(self.headers.get('Authorization','')) if (auth_map and auth_map['type']=='basic' and auth_map['username']+':'+auth_map['password']==IIIFRequestHandler.USER_PASS): # authz, continue pass else: # failed, send 401 with null image info, no service link for auth as # basic auth is all done via the WWW-Authenticate header i = IIIFInfo(api_version=self.api_version) i.identifier = 'null' i.width = 0 i.height = 0 return self.send_json_response(json=i.as_json(),status=401) if (self.iiif.info): # get size self.manipulator.srcfile=file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image i = IIIFInfo(conf=IIIFRequestHandler.INFO,api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height i.qualities = [ "native", "color" ] #FIXME - should come from manipulator i.formats = [ "jpg", "png" ] #FIXME - should come from manipulator return self.send_json_response(i.as_json(),200) else: if (self.api_version<'2.0' and self.iiif.format is None and 'Accept' in self.headers): # In 1.0 and 1.1 conneg was specified as an alternative to format, see: # http://iiif.io/api/image/1.0/#format # http://iiif.io/api/image/1.1/#parameters-format formats = { 'image/jpeg': 'jpg', 'image/tiff': 'tif', 'image/png': 'png', 'image/gif': 'gif', 'image/jp2': 'jps', 'application/pdf': 'pdf' } accept = do_conneg( self.headers['Accept'], formats.keys() ) # Ignore Accept header if not recognized, should this be an error instead? if (accept in formats): self.iiif.format = formats[accept] (outfile,mime_type)=self.manipulator.derive(file,iiif) return self.send_good_response(open(outfile,'r'),mime_type)
def do_GET(self): """Implement the HTTP GET method The bulk of this code is wrapped in a big try block and anywhere within the code may raise an IIIFError which then results in an IIIF error response (section 5 of spec). """ self.compliance_level=None # We take prefix to see whe implementation to use, / is special info if (self.path == '/'): return self.send_top_level_index_page() # Test code... m = re.match(r'/response_test/(\d\d\d)$', self.path) if (m): status=int(m.group(1)) # WHAT DOES NULL info.json look like? # https://github.com/IIIF/auth/issues/2 self.api_version='2.0' i = IIIFInfo(api_version=self.api_version) i.identifier = 'abc' i.width = 0 i.height = 0 i.service = { '@context': 'http://example.org/auth_context.json', '@id': "http://example.com/authn_here", 'label': "Authenticate here" } return self.send_json_response(json=i.as_json(),status=status) # Is this a request for a prefix index page? m = re.match(r'/([\w\._]+)$', self.path) if (m and m.group(1) in IIIFRequestHandler.MANIPULATORS): return self.send_prefix_index_page(m.group(1)) # Is this a request for a test page? m = re.match(r'/([\w\._]+)$', self.path) if (m): page_path = os.path.join(IIIFRequestHandler.PAGES_DIR,m.group(1)) if (os.path.isfile(page_path)): return self.send_html_file(page_path) # Now assume we have an iiif request m = re.match(r'/([\w\._]+)/(.*)$', self.path) if (m): self.prefix = m.group(1) self.path = m.group(2) if (self.prefix in IIIFRequestHandler.MANIPULATORS): self.api_version = IIIFRequestHandler.MANIPULATORS[self.prefix]['api_version'] self.iiif = IIIFRequest(baseurl='/'+self.prefix+'/',api_version=self.api_version) self.manipulator = IIIFRequestHandler.MANIPULATORS[self.prefix]['klass'](api_version=self.api_version) self.auth_type = IIIFRequestHandler.MANIPULATORS[self.prefix]['auth_type'] else: # 404 - unrecognized prefix self.send_404_response("Not Found - prefix /%s/ is not known" % (self.prefix)) return else: # 404 - unrecognized path structure self.send_404_response("Not Found - path structure not recognized") return try: self.do_GET_body() except IIIFError as e: e.text += " Request parameters: "+str(self.iiif) self.write_error_response(e) except Exception as ue: # Anything else becomes a 500 Internal Server Error sys.stderr.write(str(ue)+"\n") self.write_error_response(IIIFError(code=500,text="Something went wrong... %s.\n"%(str(ue))))