http_pipe_common.py :  » Development » SnapLogic » snaplogic » server » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » SnapLogic 
SnapLogic » snaplogic » server » http_pipe_common.py
# $SnapHashLicense:
# 
# SnapLogic - Open source data services
# 
# Copyright (C) 2008-2009, SnapLogic, Inc.  All rights reserved.
# 
# See http://www.snaplogic.org for more information about
# the SnapLogic project. 
# 
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the LEGAL file
# at the top of the source tree.
# 
# "SnapLogic" is a trademark of SnapLogic, Inc.
# 
# 
# $

# $Id: http_pipe_common.py 7878 2009-06-12 20:30:20Z dhiraj $
__docformat__ = "epytext en"

import os.path
import urlparse
import urllib

from snaplogic.server import RhResponse
from snaplogic.server import repository
from snaplogic.server import auth
from snaplogic.snapi_base import keys
from snaplogic.snapi_base.resdef import ResDef
from snaplogic import rp

class PipeSessionInfo(object):
    """
    Objects of this class type contain information needed to process client GET/POST requests.
    
    The HTTP session from client to SnapLogic data service can involve a lot of
    modifying factors like content-type, accept headers, sn.* params etc. Objects of this
    class are meant to be instantiated for each such HTTP session and are used to keep track
    of all these modifying factors, and are passed around various methods of the data service
    which need to be aware of these modifying factors while processing the HTTP request.
    
    """
    def __init__(self, http_req, pipe_to_http_uri_prefix):
        
        self.remaining_path = http_req.path[len(pipe_to_http_uri_prefix):]
        
        self.http_req = http_req
        
        self.rp_writer = None
        
        self.starting_rec = None
        """
        The number of the starting record. If it is None, then the first record
        of the stream will be the starting record.
        """
        
        self.rec_limit = None
        """
        The maximum number of records to return. If it is None, then no upper limit
        is placed on the number of records being returned.
        """
        
        self.count_req = None
        """
        If count_req is not set to None, then it specifies the type of objects to count.
        Currently, the only type of object that is counted is "records".
        """
        
        self.content_type = None
        """The content type being requested."""
        
        self.rp_header = None
        """
        Some RPs (like JSONP) allow a header to be specified at initialization time. This header
        is returned by the RP at the beginning of the output stream.
        """
        
        self.stream_footer = ""
        """
        Some RPs (like JSONP) allow a footer to be specified at initialization time. This footer
        is returned by the RP at the end output stream, to mark the end of output.
        """
        
        self.num_written_out = 0
        """Number of records or bytes written out so far."""
        
        self.current_record_num = 0
        """The current record being processed (1 to n)."""
        
        self.sent_headers = False
        """Flag that indicates if response header has been sent or not."""
        
        self.output_is_record = None
        """Flag if True, means that the output view is record mode view (as opposed to binary mode)."""
        
        self.input_is_record = None
        """Flag if True, means that the input view is record mode view (as opposed to binary mode)."""
        
        self.output_view_name = None
        """Name specified by the request for the resource output view (if any)."""
        
        self.input_view_name = None
        """Name specified by the request for the resource input view (if any)."""  
        
        self.resdef = None
        """ResDef being executed."""

# Params that can only be specified by the client when an output stream is being requested.
snap_output_stream_params = ["sn.start", "sn.limit", "sn.count", "sn.stream_header",
                             "sn.stream_footer", "sn.content_type"]
# Other SnapLogic specific params.
snap_other_params = ["sn.output_view"]

# All SnapLogic params.
snap_all_params = snap_output_stream_params + snap_other_params

def process_http_params(http_req, pinfo):
    """
    Process the params provided with HTTP request.
    
    @param http_req:        HTTP request object.
    @type  http_req:        L{HttpRequest}
    
    @param pinfo:           The current client session information.
    @type pinfo:            L{PipeSessionInfo}

    @return:                Return None, if the processing went fine, else, return RhResponse
                            object with data and code to be written to client.
    @rtype:                 L{RhResponse}
    
    """
    
    for a in http_req.params:
        if a.startswith("sn.") and a not in snap_all_params:
            return RhResponse(http_req.BAD_REQUEST,
                              "Parameter '%s' is not a valid SnapLogic parameter" % a)
            
    view_name =  http_req.params.get("sn.output_view")
    if view_name is not None:
        if http_req.method != 'POST':
            return RhResponse(http_req.BAD_REQUEST,
                              "Parameter 'sn.output_view' can only be specified for POST requests")
        else:
            pinfo.output_view_name = view_name
        
    if pinfo.output_view_name is None:
        # Some params can only be specified when an output stream is being returned.
        for param_name in snap_output_stream_params:
            if param_name in http_req.params:
                return RhResponse(http_req.BAD_REQUEST,
                                  "Parameter '%s' can only be specified if an output stream is being requested"
                                   % param_name)
                
                
    if "sn.start" in http_req.params:
        if not http_req.params["sn.start"].isdigit():
            return RhResponse(http_req.BAD_REQUEST, "sn.start should be a positive, non zero numeric value <%s>"
                                                     % http_req.params["sn.start"])
        pinfo.starting_rec = long(http_req.params["sn.start"])
        if pinfo.starting_rec < 1:
            return RhResponse(http_req.BAD_REQUEST, "sn.start should be a positive, non zero numeric value <%s>"
                                                     % http_req.params["sn.start"])

    if "sn.limit" in http_req.params:
        if not http_req.params["sn.limit"].isdigit():
            return RhResponse(http_req.BAD_REQUEST,  "sn.limit should be a positive numeric value <%s>"
                                                      % http_req.params["sn.limit"])
        pinfo.rec_limit = long(http_req.params["sn.limit"])
        if pinfo.rec_limit < 0:
            return RhResponse(http_req.BAD_REQUEST, "sn.limit should be a positive numeric value <%s>"
                                                     %  http_req.params["sn.limit"])
    
    if "sn.count" in http_req.params:
        if pinfo.starting_rec is not None:
            return RhResponse(http_req.BAD_REQUEST,
                              "Request cannot have both sn.start and sn.count parameters.")
        if pinfo.rec_limit is not None:
            return RhResponse(http_req.BAD_REQUEST,
                              "Request cannot have both sn.limit and sn.count parameters.")
        if http_req.params["sn.count"].lower() == "records":
            pinfo.count_req = "records"
        else:
            return RhResponse(http_req.BAD_REQUEST,  "sn.count takes the argument \"records\" and not \"%s\""
                                                      % http_req.params["sn.count"])
    
    if "sn.stream_header" in http_req.params:
        pinfo.rp_header = http_req.params["sn.stream_header"]
        
    if "sn.stream_footer" in http_req.params:
        pinfo.stream_footer = http_req.params["sn.stream_footer"]
        
    if "sn.content_type" in http_req.params:
        pinfo.content_type = http_req.params["sn.content_type"].lower()
    else:
        # TODO: Code copied from http_request.py. would be nice to have this as a utility method in http_req.
        if http_req.human_request:
            # IE for some reason doesn't indicate text/html as an acceptable
            # content type, only '*/*' and other junk. So, if the human flag
            # is set, we just have to manually choose the appropriate content
            # type. Otherwise, we would default to 'application/x-snap-asn1' and IE
            # wouldn't like that.
            pinfo.content_type = "text/html"
        else:
            pinfo.content_type = http_req.http_accept
        
    return None

def process_http_uri(http_req, pinfo):
    """
    Process the URI provided with the HTTP request to feed URI.
    
    @param http_req: HTTP request object.
    @type  http_req: L{HttpRequest}
    
    @param pinfo:    The current client session information.
    @type pinfo:     L{PipeSessionInfo}

    @return:         Return None, if the processing went fine, else, return RhResponse
                     object with data and code to be written to client.
    @rtype:          L{RhResponse}
    
    """
    # Get resource URI and view name.            
    (pinfo.resource_uri, view_name) = os.path.split(pinfo.remaining_path)
    
    if not pinfo.resource_uri:
        return RhResponse(http_req.NOT_FOUND, "Please specify a resource in the URI")
    
    if not view_name:
        return RhResponse(http_req.NOT_FOUND, "URI '%s' does not specify a view name" % http_req.path)
    
    # Before we waste any more cycles on this request, check the authorization for this request.
    perms = auth.check_uri(pinfo.resource_uri, http_req.username, http_req.groups)
    if not auth.can_exec(perms):
        return RhResponse(http_req.UNAUTHORIZED, "Not authorized to access resource '%s' in URI '%s'" %
                                               (pinfo.resource_uri, http_req.path))
    
    # Next, lets make sure that the resource still exists in the repository.
    rep = repository.get_instance()
    ret = rep.read_resources([pinfo.resource_uri])[keys.SUCCESS]
    if pinfo.resource_uri not in ret:
        return RhResponse(http_req.NOT_FOUND, "URI '%s' specifies unknown resource '%s'" %
                          (http_req.path, pinfo.resource_uri))
    pinfo.guid = ret[pinfo.resource_uri][keys.GUID]
    pinfo.gen_id = str(ret[pinfo.resource_uri][keys.GEN_ID])
    resdef_dict = ret[pinfo.resource_uri][keys.RESDEF]
    resdef = ResDef(resdef_dict)
    pinfo.resdef = resdef
    if http_req.method == 'GET':
        # This is a request to the output view of a resource.
        if view_name not in resdef.list_output_view_names():
            return RhResponse(http_req.NOT_FOUND, "Output view '%s' not found in resource '%s'. Available views : %s."
                                                  % (view_name, pinfo.resource_uri, resdef.list_output_view_names()))
        pinfo.output_is_record = resdef.output_is_record_mode(view_name)
        pinfo.output_view_name = view_name
    elif http_req.method == 'POST':
        # This is a request to the input view of a resource.
        if view_name not in resdef.list_input_view_names():
            return RhResponse(http_req.NOT_FOUND, "Input view '%s' not found in resource '%s'. Available views : %s."
                                                  % (view_name, pinfo.resource_uri, resdef.list_input_view_names()))
        pinfo.input_is_record = resdef.input_is_record_mode(view_name)
        pinfo.input_view_name = view_name
        
    ret = process_http_params(http_req, pinfo)
    if ret is not None:
        return ret
    
    if http_req.method == 'POST' and pinfo.output_view_name is not None:
        if pinfo.output_view_name not in resdef.list_output_view_names():
            return RhResponse(http_req.BAD_REQUEST,
                              "Output view '%s' not found in resource '%s'. Available views : %s."
                              % (pinfo.output_view_name, pinfo.resource_uri, resdef.list_output_view_names()))
        pinfo.output_is_record = resdef.output_is_record_mode(pinfo.output_view_name)
    
    #
    # First, lets negotiate content for output stream. Irrespective of whether we are handling a POST or
    # a GET, we need to have an output stream, as even a POST needs to send data back, like some kind of
    # error message.
    #
    if pinfo.output_is_record == True:
        if pinfo.content_type == rp.SNAP_ASN1_CONTENT_TYPE:
            return RhResponse(http_req.UNSUPPORTED_MEDIA_TYPE, "URI %s does not support content type '%s'."
                                                                % (http_req.path, pinfo.content_type))
        out_rp = rp.get_rp(pinfo.content_type)
        if out_rp is None:
            return RhResponse(http_req.UNSUPPORTED_MEDIA_TYPE, "URI %s does not support content type '%s'."
                                                               % (http_req.path, pinfo.content_type))
        pinfo.content_type = out_rp.CONTENT_TYPE
    elif pinfo.output_is_record == False:
        # This is either binary output view or a POST that is not reading any output view. For
        # this scenario the params sn.count, sn.start and sn.limits are not supported.
        if pinfo.rec_limit is not None:
            return RhResponse(http_req.BAD_REQUEST, "Request to binary view cannot have sn.limit parameter.")
        if pinfo.starting_rec is not None:
            return RhResponse(http_req.BAD_REQUEST, "Request to binary view cannot have sn.start parameter.")
        if pinfo.count_req is not None:
            return RhResponse(http_req.BAD_REQUEST, "Request to binary view cannot have sn.count parameter.")
        
        ctypes = resdef.get_output_view_content_types(view_name)
        matched_ctx = rp.negotiate_content_type_str(pinfo.content_type, ctypes)
        if matched_ctx is None:
            return RhResponse(http_req.UNSUPPORTED_MEDIA_TYPE, "URI %s does not support content type '%s'."
                                                               % (http_req.path, pinfo.content_type))
        pinfo.content_type = matched_ctx
    else:
        # No output view is going to be redirected here. We can respond with one of our RP supported
        # content types.
        out_rp = rp.get_rp(pinfo.content_type)
        if out_rp is None:
            return RhResponse(http_req.UNSUPPORTED_MEDIA_TYPE, "URI %s does not support content type '%s'."
                                                               % (http_req.path, pinfo.content_type))
            
def write_http_output(pinfo, data):
    """
    This method writes output to the client that made the request to this component.
    
    @param pinfo: The current client session information.
    @type pinfo:  L{PipeSessionInfo}
    
    @param data:  The data that needs to be written out.
    @type data:   list or str
    
    """
    
    if not pinfo.sent_headers:
        send_response_headers(pinfo, 200)
        
    if pinfo.rp_writer is not None:
        pinfo.rp_writer.write(data)
    else:
        pinfo.http_req._output.write(data)
            
def end_output(pinfo):
    """
    Puts an end to the data being returned to client.
    
    @param pinfo: The current client session information.
    @type pinfo:  L{PipeSessionInfo}
    
    """
    if not pinfo.sent_headers:
        send_response_headers(pinfo, 200)
                
    if pinfo.rp_writer is not None:
        pinfo.rp_writer.end(pinfo.stream_footer)
        pinfo.rp_writer = None
        
def send_response_headers(pinfo, http_code):
    """
    Sends response headers for the HTTP request from the client.
    
    @param pinfo:     The current client session information.
    @type pinfo:      L{PipeSessionInfo}
        
    @param http_code: The HTTP code to be returned in the response HTTP header.
    @type http_code:  int
    
    """
    pinfo.http_req.send_response_headers(http_code, (('content-type', pinfo.content_type),), False)
    pinfo.sent_headers = True

    if pinfo.output_is_record:
        r = rp.get_rp(pinfo.content_type)
        pinfo.rp_writer = r.Writer(pinfo.http_req._output, None, False, { 'record_stream' : 'yes' })
        if pinfo.rp_header:
            pinfo.rp_writer.initialize(pinfo.rp_header)
        else:
            pinfo.rp_writer.initialize()
            
def write_rec_to_http(pinfo, rec):
    """
    Writes a record to the client that made the request.
    
    @param pinfo: The current client session information.
    @type pinfo:  L{PipeSessionInfo}
    
    @param rec:   The raw record (tuple) to be written out
    @type rec:    tuple.
    
    @return:      True, if the number of records written has hit the limit specified by the client, else, False.
    @rtype:       bool
    
    """
    if pinfo.count_req is not None:
        # This is only a request to count records
        return False
            
    if pinfo.starting_rec is not None and  pinfo.current_record_num < pinfo.starting_rec:
        return False
    if pinfo.rec_limit is not None and (pinfo.num_written_out >= pinfo.rec_limit):
        return True
            
    write_http_output(pinfo, rec)
    pinfo.num_written_out += 1
    return False
    

    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.