HttpPost.py :  » Development » SnapLogic » snaplogic » components » 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 » components » HttpPost.py
# $SnapHashLicense:
# 
# SnapLogic - Open source data services
# 
# Copyright (C) 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: HttpPost.py 10330 2009-12-24 22:13:38Z grisha $
"""
HttpPost module POSTs or PUTs data from binary input view to a URL
and returns server response in output views 
"""
import os

import snaplogic.components.FileUtils as FileUtils
from snaplogic.common import snap_log
from snaplogic.common.snap_exceptions import *
from snaplogic.common import version_info
from snaplogic.cc import component_api
from snaplogic.cc.component_api import ComponentAPI
import snaplogic.cc.prop as prop
from snaplogic.snapi_base import keys
from snaplogic.common.data_types import SnapString,SnapNumber
from snaplogic.common import snap_http_lib
from snaplogic import rp
from decimal import Decimal

ALLOWED_SCHEMES = ["http", "https"]

class HttpPost(ComponentAPI):
    """
    HttpPost
    """
    api_version = '1.0'
    component_version = '1.1'

    capabilities = {
        ComponentAPI.CAPABILITY_INPUT_VIEW_LOWER_LIMIT    : 1,
        ComponentAPI.CAPABILITY_INPUT_VIEW_UPPER_LIMIT    : 1,
        ComponentAPI.CAPABILITY_OUTPUT_VIEW_LOWER_LIMIT   : 1,
        ComponentAPI.CAPABILITY_OUTPUT_VIEW_UPPER_LIMIT   : 2,
        ComponentAPI.CAPABILITY_ALLOW_PASS_THROUGH        : False,
        ComponentAPI.CAPABILITY_INPUT_VIEW_ALLOW_BINARY   : True,
        ComponentAPI.CAPABILITY_OUTPUT_VIEW_ALLOW_BINARY  : True
    }
    
    component_description = "This component POSTs or PUTs data to a URL"
    component_label       = "HTTP Post/Put"
    component_doc_uri      = "https://www.snaplogic.org/trac/wiki/Documentation/%s/ComponentRef/HttpPost" % \
                                                        version_info.doc_uri_version

    http_methods = ['POST', 'PUT']
    
    def create_resource_template(self):
        """
        Create HttpPost resource definition template. It consists of
        
        outputURL:    The URL to post or put data to
        username:     Credentials: username for that URL
        password:     Credentials: password for that URL
        HttpMethod:    One of [PUT, POST]
        """
        self.set_property_def('outputURL',
                              prop.SimpleProp("Output URL", SnapString, "URL to post", None, True))

        self.set_property_def("username",
                              prop.SimpleProp("Credentials:  Username", SnapString, 
                                              "Username to use if credentials are needed for the accessing data"))
        self.set_property_value("username", "")
        
        self.set_property_def("password",
                              prop.SimpleProp("Credentials: Password", SnapString, 
                                              "Password to use if credentials are needed for the accessing data", 
                                              {'obfuscate' : 0}))

        self.set_property_def('HttpMethod',
                              prop.SimpleProp("Http Method", SnapString, "Http Method",
                                              {"lov": self.http_methods}, True))
        
        # Set the default output views.
        self.add_record_output_view_def("response_rec",
                  [
                   ('response_code',    SnapNumber, ""),
                   ('response_reason',  SnapString, ""),
                   ('response_body',    SnapString, ""),
                   ('response_headers', SnapString, "")
                  ], "Response", False)
        
        # The *default* content type of binary output view is bogus SNAP_MAGIC_CONTENT_TYPE.
        # This is to force whoever is reading from it to accept "*/*" because
        # the actual content type is not known in advance; it should be whatever
        # the URL this resource is *posting to* returns back, which may not be known
        # in advance. If user knows the content type, they can override this by
        # setting it for this view.
        self.add_binary_output_view_def("response_bin", (rp.SNAP_MAGIC_CONTENT_TYPE,), 'Response', True)
        self.add_binary_input_view_def('httppostin', ("*/*",), "Input", True)
        
        
    def validate(self, err_obj):
        """
        Validate HttpPost resource for correctness.
        """
        # For now, this component only supports binary input view.
        # It could potentially be expanded to do a form post from a record input view.
        input_views = self.list_input_view_names()
        input_view = self.get_input_view_def(input_views[keys.SINGLE_VIEW])
        if input_view["is_record"] is not False:
            err_obj.get_input_view_err()[input_views[keys.SINGLE_VIEW]].set_message(
                    "Input view has to be binary.")

        # Make sure output views, if two are defined, are one of record, and the other -
        # of binary kind.
        output_views = self.list_output_view_names()
        view1 = None
        view2 = None
        try:
            view1 = self.get_output_view_def(output_views[0])
            view2 = self.get_output_view_def(output_views[1])
            if bool(int(view1["is_record"]) ^ int(view2["is_record"])) is False:
                err_obj.get_output_view_err()[output_views[keys.SINGLE_VIEW]].set_message(
                    "If two output views are defined, one has to be a record and the other - a binary view.")
        except IndexError:
            # output views are optional
            pass
 

    def execute(self, input_views, output_views):
        try:
            input_view = input_views.values()[keys.SINGLE_VIEW] 
        except IndexError:
            raise SnapComponentError("No output view connected.")

        # We want to allow output view(s) to be defined but not connected.
        view1 = None
        view2 = None
        output_view_bin = None
        output_view_rec = None
        try:
            view1 = output_views.values()[0]
            if view1.is_binary:
                output_view_bin = view1
            else:
                output_view_rec = view1
            view2 = output_views.values()[1]
            if view2.is_binary:
                output_view_bin = view2
            else:
                output_view_rec = view2
        except IndexError:
            pass
            
        outputurl = self.get_property_value("outputURL")
        username = self.get_property_value("username")
        password = self.get_property_value("password")
        outputurl = FileUtils.qualify_filename(outputurl)
        method = self.get_property_value("HttpMethod")
        # Validate filename URI scheme
        error = FileUtils.validate_file_scheme(outputurl, ALLOWED_SCHEMES, ALLOWED_SCHEMES)
        if error is not None:
            raise SnapComponentError(error)
        
        # "pass" input view's content type 
        ctype = input_view.get_binary_content_type()
        hdr = {}
        hdr["Content-Type"] = ctype
        
        # If the user chooses to manually set the output view content type, then
        # that should be specified in the Accept header of the POST request.
        # Otherwise the default Accept is "*/*".
        acceptStr = ""
        first = True
        if output_view_bin is not None:
            for ctype in output_view_bin.list_binary_content_types():
                if not first:
                    acceptStr += ','
                if ctype == rp.SNAP_MAGIC_CONTENT_TYPE:
                    acceptStr += "*/*"
                    break
                else:
                    acceptStr += ctype
                    first = False
        hdr["Accept"] = acceptStr
        hdr["Accept-Charset"] = "*"
        
        # read all data from the input to post it in one chunk
        res_line = input_view.read_binary(500)
        result_data = ""
        while res_line is not None:
            result_data += res_line
            res_line = input_view.read_binary(500)

        if username != "":
            _creds = (username, password)
        else:
            _creds = None
        
        response = snap_http_lib.sendreq(method, outputurl, result_data, hdr, _creds)
        response.getResponse()
        code = response.getStatus()
        reason = response.getReason()
        headers_dic = response.getHeaders()
        headers = ""
        encoding = ""
        respType = ""
        for key in headers_dic.keys():
            # Looking for charset in e.g.
            # "Content-Type: text/html; charset=ISO-8859-4"
            # of the first content type.
            if key.lower() == "content-type" :
                if respType == "":
                    respType = headers_dic[key]
                    if respType.find("text/") != -1 :
                        tpl = respType.partition('=')
                        encoding = tpl[2].strip()
            # Lump all headers together, since we can't have dynamic number of fields in the output view
            headers = headers + key + ":" + headers_dic[key] + '\n'
        
        # If no charset found, default to utf-8
        if encoding == "":
            encoding = 'utf-8'
        
        response_body = response.read()
        if output_view_rec is not None:
            out_rec = output_view_rec.create_record()
            try:
                out_rec['response_code'] = Decimal(code)
            except:
                out_rec['response_code'] = Decimal(500)
            try:
                out_rec['response_reason'] = unicode(reason, 'utf-8')
            except:
                out_rec['response_reason'] = None
            try:
                out_rec['response_body'] = unicode(response_body, encoding)
            except:
                out_rec['response_body'] = None
            out_rec['response_headers'] = unicode(headers, 'utf-8')
            output_view_rec.write_record(out_rec)
            output_view_rec.completed()

        # write to the binary output view
        if output_view_bin is not None:
            # The server response will have the content-type that the server
            # has picked from the the choices in Accept header of the POST request.
            # We use that type to write binary output (no need to sanity-check
            # it because it will have to be one that we (user) supposedly accept.)
            types = output_view_bin.list_binary_content_types()
            output_view_bin.write_binary(response_body, types[0])
            output_view_bin.completed()
        
        response.close()
    
    def upgrade_1_0_to_1_1(self):
        """
        No-op upgrade only to change component doc URI during the upgrade
        which will be by cc_info before calling this method.
        
        """
        pass
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.