Commits

Peter Nixon committed 86f8d59

Add REQMOD support and some extra cli parameters

  • Participants
  • Parent commits 2f73b01

Comments (0)

Files changed (1)

File icapclient.py

 #! /usr/bin/env python
 """
-A bare bones ICAP client that can be used to check if an ICAP service is responding to OPTIONS requests
+A bare bones ICAP client that can generate basic OPTIONS and REQMOD requests
 
 The command line parameters are designed to be compatible with OpenNMS GpMonitor (and hopefully Nagios)
 """
 
+import sys
 from twisted.internet import reactor
 from twisted.protocols.basic import LineReceiver
 from twisted.python import log
-import sys
+from urlparse import urlparse
 
 __author__ = "Peter Nixon"
 __copyright__ = "Copyright (C) 2013 Peter Nixon"
 __license__ = "Public Domain"
-__version__ = "1.0"
+__version__ = "1.1"
 
-DEBUG = False
-
-OPTIONS_REQUEST = """OPTIONS icap://localhost/service ICAP/1.0\r
-Host: localhost\r
-Encapsulated: null-body=0\r
-\r
-"""
+DEFAULT_ICAP_URI = "icap://localhost/service"
+DEFAULT_HTTP_URL = "http://t.cphone.org/"
 
 class IcapProtocol(LineReceiver):
+    delimiter = "\r\n"
+    
+    def __init__(self, icapMethod, icapService, requestURL):
+        self.icapMethod = icapMethod
+        self.icapService = icapService
+        self.requestURL = requestURL
+        self.requestHost = urlparse(self.requestURL).hostname
 
-    #def makeConnection(self, transport):
-    #    print transport       
-
-    def connectionLost(self, reason):
-        #print reason
-        self.sendData = False
-
-    def connectionMade(self):
-        #print "connection made"
-        self.delimiter = "\n"
-        self.sendData = True
-        #print self.transport
-        self.sendRequest()
-
-    def lineReceived(self, dline):
-        line = dline.rstrip()
+    def processIcap(self, line):
+        '''Process ICAP responses and quit with the correct status'''
+        log.msg(line)
         if line == "ICAP/1.0 200 OK":
             self.printSuccess()
-        elif line == "ICAP/1.0 400 Bad Request":
+        elif line.startswith('ICAP/1.0 4'):
+            '''Bad request'''
             self.printFailure()
-        elif line == "ICAP/1.0 500 Server error":
+        elif line.startswith('ICAP/1.0 5'):
+            '''Server error'''
             self.printFailure()
         else:
             print "Hmm server said:", line
 
+    def connectionLost(self, reason):
+        log.msg('Lost client connection.  Reason: %s' % reason)
+        self.sendData = False
+
+    def connectionMade(self):
+        self.sendData = True
+        log.msg("connection made")
+        log.msg(self.transport)
+        self.sendRequest()
+
+    def lineReceived(self, line):
+        self.processIcap(line)
+
+    def generateRequest(self):
+        request = None
+        if self.icapMethod == "OPTIONS":
+            request = "OPTIONS %s ICAP/1.0\r\n" % (self.icapService)
+            request += "Host: localhost\r\n"
+            request += "Encapsulated: null-body=0\r\n"
+            request += "\r\n"
+        elif self.icapMethod == "REQMOD":
+            httpHeaders = "GET %s HTTP/1.1\r\n" % (self.requestURL)
+            httpHeaders += "Host: %s\r\n" % (self.requestHost)
+            httpHeaders += "Connection: close\r\n"
+            httpHeaders += "Accept-Encoding: gzip\r\n"
+            
+            request = "REQMOD %s ICAP/1.0\r\n" % (self.icapService)
+            request += "Host: localhost\r\n"
+            request += "Encapsulated: req-hdr=0, null-body=%i\r\n" % (len(httpHeaders))
+            request += "\r\n"
+            request += httpHeaders
+            request += "\r\n"
+            
+        if (request): log.msg(request)
+        else: log.msg("Unsupported request type")
+        return request
+        
     def sendRequest(self):
-        self.transport.write(OPTIONS_REQUEST)
+        if self.icapMethod == "OPTIONS" or self.icapMethod == "REQMOD":
+            log.msg("Sending %s request" % (self.icapMethod))
+            icapRequest = self.generateRequest()
+            self.transport.write(icapRequest)
+        else:
+            print "FAILURE: The ICAP method you specified is not supported by this client"
+            self.transport.loseConnection()
 
     def printSuccess(self):
         print "SUCCESS"
         print "FAILURE"
         self.transport.loseConnection()
 
-class myProtocolFactory():
+class IcapProtocolFactory():
     protocol = IcapProtocol
+    
+    def __init__(self, icapMethod="OPTIONS", icapService=DEFAULT_ICAP_URI, requestURL=DEFAULT_HTTP_URL):
+        self.icapMethod = icapMethod
+        self.icapService = icapService
+        self.requestURL = requestURL
 
     def doStart(self):
         pass
 
     def startedConnecting(self, connectorInstance):
-        #print connectorInstance
         pass
 
     def buildProtocol(self, address):
-        #print address
-        return self.protocol()
+        return self.protocol(self.icapMethod, self.icapService, self.requestURL)
 
     def clientConnectionLost(self, connection, reason):
         log.msg(connection)
         log.msg(reason)
-        #print reason
-        #print connection
         reactor.stop()
 
     def clientConnectionFailed(self, connection, reason):
 
     parser.add_option("-H", "--hostname", dest="hostname", default=None,
                       help='ICAP Server hostname or IP address (default: None)')
-                      
+
+    parser.add_option("-m", "--method", dest="method", default="OPTIONS",
+                      help='ICAP request method to use (default: OPTIONS)')
+
+    parser.add_option("-s", "--service", dest="service", default=DEFAULT_ICAP_URI,
+                      help='ICAP service URI to use for requests (default: %s)' % (DEFAULT_ICAP_URI))
+
+    parser.add_option("-u", "--url", dest="url", default=DEFAULT_HTTP_URL,
+                      help='HTTP URL to use for REQMOD requests (default: %s)' % (DEFAULT_HTTP_URL))
+
     parser.add_option('-p', '--port', dest='port', default=1344, type='int',
                       help='TCP port to send ICAP request to (default: 1344)')
 
-    parser.add_option('-t', '--timeout', dest='timeout', default=3.0, type='float',
+    parser.add_option('-t', '--timeout', dest='timeout', default=3, type='int',
                       help='Timeout in seconds before failing automatically')
 
+    parser.add_option('-v', '--verbose', dest="verbose", action="store_true",
+                      help='Enable debug messages')
+
     (options, args) = parser.parse_args()
 
+    if (options.verbose): log.startLogging(sys.stdout)
+
     if (options.hostname):
-        reactor.connectTCP(options.hostname, options.port, myProtocolFactory(), timeout=options.timeout)
+        reactor.connectTCP(options.hostname, options.port, IcapProtocolFactory(options.method, options.service, options.url), timeout=options.timeout)
         reactor.run()
     else:
         parser.print_help()
         sys.exit(1)
         
 if __name__ == '__main__':
-    if DEBUG: log.startLogging(sys.stdout)
     main()