icap

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 8, 2025 License: BSD-3-Clause Imports: 17 Imported by: 0

README

ICAP - Internet Content Adaptation Protocol for Go

GoDoc Go Report Card

A Go library implementing the Internet Content Adaptation Protocol (ICAP) as defined in RFC 3507.

Overview

ICAP is a lightweight protocol for executing a "remote procedure call" on HTTP messages. It is used to extend transparent proxy servers by providing transformation, antivirus scanning, content filtering, ad blocking, and other value-added services.

This library provides both server and client implementations of the ICAP protocol.

Installation

go get github.com/intra-sh/icap

Usage

Creating a simple ICAP server
package main

import (
	"fmt"
	"github.com/intra-sh/icap"
	"net/http"
)

func main() {
	// Handle REQMOD requests at the /reqmod endpoint
	icap.HandleFunc("/reqmod", reqmodHandler)

	// Start the server
	fmt.Println("Starting ICAP server on port 1344...")
	if err := icap.ListenAndServe(":1344", nil); err != nil {
		fmt.Println("Error starting server:", err)
	}
}

// reqmodHandler handles REQMOD requests
func reqmodHandler(w icap.ResponseWriter, req *icap.Request) {
	h := w.Header()
	h.Set("ISTag", "\"GOLANG\"")
	h.Set("Service", "ICAP Go Service")

	switch req.Method {
	case "OPTIONS":
		h.Set("Methods", "REQMOD")
		h.Set("Allow", "204")
		h.Set("Preview", "0")
		h.Set("Transfer-Preview", "*")
		w.WriteHeader(200, nil, false)
	case "REQMOD":
		// Modify the request here
		if req.Request != nil {
			// For example, add a header to the HTTP request
			req.Request.Header.Add("X-ICAP-Processed", "true")
		}
		w.WriteHeader(200, req.Request, false)
	default:
		w.WriteHeader(405, nil, false)
		fmt.Println("Invalid request method")
	}
}
Using the bridge to serve HTTP content locally
package main

import (
	"github.com/intra-sh/icap"
)

func handler(w icap.ResponseWriter, req *icap.Request) {
	// ServeLocally allows you to use the local HTTP server to generate responses
	icap.ServeLocally(w, req)
}

func main() {
	// Set up an HTTP handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, ICAP bridged to HTTP!")
	})

	// Set up the ICAP handler
	icap.HandleFunc("/icap-bridge", handler)

	// Start the ICAP server
	icap.ListenAndServe(":1344", nil)
}

Working with Request Modification (REQMOD)

Request modification mode allows the ICAP server to examine and modify HTTP requests before they reach the origin server.

Accessing ICAP Headers
func reqmodHandler(w icap.ResponseWriter, req *icap.Request) {
    // Read ICAP request headers
    preview := req.Header.Get("Preview")
    
    // Set ICAP response headers
    h := w.Header()
    h.Set("ISTag", "\"GOLANG-TAG\"")
    h.Set("Service", "ICAP Go Service")
    
    // Process request based on ICAP method
    switch req.Method {
        // ... handler code
    }
}
Working with Encapsulated HTTP Request
func reqmodHandler(w icap.ResponseWriter, req *icap.Request) {
    // ... ICAP headers handling
    
    if req.Request != nil {
        // Access HTTP request method, URL, and version
        httpMethod := req.Request.Method
        httpURL := req.Request.URL.String()
        
        // Access HTTP request headers
        userAgent := req.Request.Header.Get("User-Agent")
        
        // Modify HTTP request headers
        req.Request.Header.Set("X-ICAP-Modified", "true")
        req.Request.Header.Add("X-ICAP-Processed-Time", time.Now().String())
        
        // Change request destination
        req.Request.Host = "new-destination.com"
        req.Request.URL.Host = "new-destination.com"
        
        // Return modified request
        w.WriteHeader(200, req.Request, false)
    }
}
Modifying HTTP Request Body
func reqmodHandler(w icap.ResponseWriter, req *icap.Request) {
    // ... ICAP and HTTP headers handling
    
    if req.Request != nil && req.Method == "REQMOD" {
        // Read original body
        originalBody, err := io.ReadAll(req.Request.Body)
        if err != nil {
            // Handle error
            w.WriteHeader(500, nil, false)
            return
        }
        
        // Modify body (simple example: convert to uppercase)
        modifiedBody := bytes.ToUpper(originalBody)
        
        // Replace body in request with modified version
        // WriteHeader's third parameter 'true' indicates we'll be writing a body
        w.WriteHeader(200, req.Request, true)
        
        // Write modified body
        w.Write(modifiedBody)
    }
}

Working with Response Modification (RESPMOD)

Response modification mode allows the ICAP server to examine and modify HTTP responses before they reach the client.

Accessing ICAP Headers in RESPMOD
func respmodHandler(w icap.ResponseWriter, req *icap.Request) {
    // Read ICAP request headers
    preview := req.Header.Get("Preview")
    
    // Set ICAP response headers
    h := w.Header()
    h.Set("ISTag", "\"GOLANG-RESP-TAG\"")
    h.Set("Service", "ICAP Respmod Service")
    
    switch req.Method {
    case "OPTIONS":
        h.Set("Methods", "RESPMOD")
        h.Set("Allow", "204")
        h.Set("Preview", "0")
        w.WriteHeader(200, nil, false)
    case "RESPMOD":
        // Response modification code
    }
}
Working with Encapsulated HTTP Response
func respmodHandler(w icap.ResponseWriter, req *icap.Request) {
    // ... ICAP headers handling
    
    if req.Response != nil {
        // Access HTTP response status
        statusCode := req.Response.StatusCode
        statusText := req.Response.Status
        
        // Access HTTP response headers
        contentType := req.Response.Header.Get("Content-Type")
        
        // Modify HTTP response headers
        req.Response.Header.Set("X-ICAP-Modified", "true")
        req.Response.Header.Add("X-ICAP-Scanned", "clean")
        
        // Access the request URL (that generated this response)
        originalURL := req.Request.URL.String()
        
        // Return modified response
        w.WriteHeader(200, req.Response, false)
    }
}
Modifying HTTP Response Body
func respmodHandler(w icap.ResponseWriter, req *icap.Request) {
    // ... ICAP and HTTP headers handling
    
    if req.Response != nil && req.Method == "RESPMOD" {
        // Read original response body
        originalBody, err := io.ReadAll(req.Response.Body)
        if err != nil {
            // Handle error
            w.WriteHeader(500, nil, false)
            return
        }
        
        // Example: modify HTML content
        modifiedBody := bytes.Replace(
            originalBody, 
            []byte("<title>"), 
            []byte("<title>Modified by ICAP: "), 
            -1,
        )
        
        // Write response with modified body
        w.WriteHeader(200, req.Response, true)
        w.Write(modifiedBody)
    }
}
Handling No Modifications

If you don't need to modify the request or response, you can use 204 status code:

// No modifications needed
w.WriteHeader(204, nil, false)

Status Codes

Common ICAP status codes:

  • 200: OK - Successful modification
  • 204: No Modifications - Content should not be modified
  • 400: Bad Request - Malformed ICAP request
  • 404: ICAP Service Not Found
  • 405: Method Not Allowed
  • 500: Server Error
  • 501: Method Not Implemented

License

BSD - See LICENSE for details

Documentation

Overview

Package icap implements the Internet Content Adaptation Protocol (ICAP) as defined in RFC 3507.

ICAP is a protocol that allows edge devices such as proxies to offload tasks to dedicated servers. It is commonly used for content filtering, antivirus scanning, and other content adaptation services.

This library provides both server and client implementations of the ICAP protocol. It allows Go programs to:

  • Create ICAP servers that can process and modify HTTP requests and responses
  • Bridge between ICAP and HTTP to serve local content
  • Handle REQMOD and RESPMOD ICAP methods

Basic usage example:

package main

import (
	"fmt"
	"github.com/intra-sh/icap"
	"net/http"
)

func main() {
	icap.HandleFunc("/example", exampleHandler)
	fmt.Println("Starting ICAP server on port 1344...")
	if err := icap.ListenAndServe(":1344", nil); err != nil {
		fmt.Println("Error starting server:", err)
	}
}

func exampleHandler(w icap.ResponseWriter, req *icap.Request) {
	h := w.Header()
	h.Set("ISTag", "\"GOLANG-ICAP\"")

	switch req.Method {
	case "OPTIONS":
		h.Set("Methods", "REQMOD, RESPMOD")
		h.Set("Allow", "204")
		w.WriteHeader(200, nil, false)
	case "REQMOD", "RESPMOD":
		w.WriteHeader(204, nil, false) // No modifications
	default:
		w.WriteHeader(405, nil, false)
	}
}

Package icap provides an extensible ICAP server.

Index

Constants

This section is empty.

Variables

View Source
var DefaultServeMux = NewServeMux()

DefaultServeMux is the default ServeMux used by Serve.

Functions

func Handle

func Handle(pattern string, handler Handler)

Handle registers the handler for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched.

func HandleFunc

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

HandleFunc registers the handler function for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched.

func ListenAndServe

func ListenAndServe(addr string, handler Handler) error

ListenAndServe listens on the TCP network address addr and then calls Serve with handler to handle requests on incoming connections.

func ListenAndServeDebug added in v0.0.5

func ListenAndServeDebug(addr string, handler Handler) error

func ListenAndServeTLS

func ListenAndServeTLS(addr, cert, key string, handler Handler) error

ListenAndServeTLS --

func NewBridgedResponseWriter

func NewBridgedResponseWriter(w ResponseWriter) http.ResponseWriter

NewBridgedResponseWriter Create an http.ResponseWriter that encapsulates its response in an ICAP response.

func NewChunkedWriter

func NewChunkedWriter(w io.Writer) io.WriteCloser

NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP "chunked" format before writing them to w. Closing the returned chunkedWriter sends the final 0-length chunk that marks the end of the stream.

NewChunkedWriter is not needed by normal applications. The http package adds chunking automatically if handlers don't set a Content-Length header. Using NewChunkedWriter inside a handler would result in double chunking or chunking with a Content-Length length, both of which are wrong.

func NotFound

func NotFound(w ResponseWriter, r *Request)

NotFound replies to the request with an HTTP 404 not found error.

func Optional added in v0.1.1

func Optional(condition bool, a string, b string) string

func Redirect

func Redirect(w ResponseWriter, r *Request, redirectURL string, code int)

Redirect replies to the request with a redirect to url, which may be a path relative to the request path.

func Serve

func Serve(l net.Listener, handler Handler) error

Serve accepts incoming ICAP connections on the listener l, creating a new service thread for each. The service threads read requests and then call handler to reply to them.

func ServeLocally

func ServeLocally(w ResponseWriter, req *Request)

ServeLocally Pass use the local HTTP server to generate a response for an ICAP request.

func ServeLocallyFromHandler

func ServeLocallyFromHandler(w ResponseWriter, req *Request, mux http.Handler)

ServeLocallyFromHandler ---

func SimulateRequestHandling added in v0.1.1

func SimulateRequestHandling(icapMethod string, inputHttpHeaders []string, httpBody string, xUrl string, handler func(ResponseWriter, *Request)) (string, error)

func StatusText

func StatusText(code int) string

StatusText returns a text for the ICAP status code. It returns the empty string if the code is unknown.

Types

type Handler

type Handler interface {
	ServeICAP(ResponseWriter, *Request)
}

Handler Objects implementing the Handler interface can be registered to serve ICAP requests.

ServeICAP should write reply headers and data to the ResponseWriter and then return.

func NotFoundHandler

func NotFoundHandler() Handler

NotFoundHandler returns a simple request handler that replies to each request with a “404 page not found” reply.

func RedirectHandler

func RedirectHandler(redirectURL string, code int) Handler

RedirectHandler returns a request handler that redirects each request it receives to the given url using the given status code.

type HandlerFunc

type HandlerFunc func(ResponseWriter, *Request)

The HandlerFunc type is an adapter to allow the use of ordinary functions as ICAP handlers. If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.

func (HandlerFunc) ServeICAP

func (f HandlerFunc) ServeICAP(w ResponseWriter, r *Request)

ServeICAP calls f(w, r).

type Request

type Request struct {
	Method     string               // REQMOD, RESPMOD, OPTIONS, etc.
	RawURL     string               // The URL given in the request.
	URL        *url.URL             // Parsed URL.
	Proto      string               // The protocol version.
	Header     textproto.MIMEHeader // The ICAP header
	RemoteAddr string               // the address of the computer sending the request
	Preview    []byte               // the body data for an ICAP preview

	// The HTTP messages.
	Request  *http.Request
	Response *http.Response
}

A Request represents a parsed ICAP request.

func ReadRequest

func ReadRequest(b *bufio.ReadWriter) (req *Request, err error)

ReadRequest reads and parses a request from b.

type ResponseWriter

type ResponseWriter interface {
	// Header returns the header map that will be sent by WriteHeader.
	// Changing the header after a call to WriteHeader (or Write) has
	// no effect.
	Header() http.Header

	// Write writes the data to the connection as part of an ICAP reply.
	// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK, nil)
	// before writing the data.
	Write([]byte) (int, error)

	// Write raw data to the connection.
	WriteRaw(string)

	// WriteHeader sends an ICAP response header with status code.
	// Then it sends an HTTP header if httpMessage is not nil.
	// httpMessage may be an *http.Request or an *http.Response.
	// hasBody should be true if there will be calls to Write(), generating a message body.
	WriteHeader(code int, httpMessage interface{}, hasBody bool)
}

ResponseWriter ---

type ServeMux

type ServeMux struct {
	// contains filtered or unexported fields
}

ServeMux is an ICAP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.

For more details, see the documentation for http.ServeMux

func NewServeMux

func NewServeMux() *ServeMux

NewServeMux allocates and returns a new ServeMux.

func (*ServeMux) Handle

func (mux *ServeMux) Handle(pattern string, handler Handler)

Handle registers the handler for the given pattern.

func (*ServeMux) HandleFunc

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))

HandleFunc registers the handler function for the given pattern.

func (*ServeMux) ServeICAP

func (mux *ServeMux) ServeICAP(w ResponseWriter, r *Request)

ServeICAP dispatches the request to the handler whose pattern most closely matches the request URL.

type Server

type Server struct {
	Addr         string  // TCP address to listen on, ":1344" if empty
	Handler      Handler // handler to invoke
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
	DebugLevel   int
}

A Server defines parameters for running an ICAP server.

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe() error

ListenAndServe listens on the TCP network address srv.Addr and then calls Serve to handle requests on incoming connections. If srv.Addr is blank, ":1344" is used.

func (*Server) ListenAndServeTLS

func (srv *Server) ListenAndServeTLS(cert, key string) error

ListenAndServeTLS ---

func (*Server) Serve

func (srv *Server) Serve(l net.Listener) error

Serve accepts incoming connections on the Listener l, creating a new service thread for each. The service threads read requests and then call srv.Handler to reply to them.

Directories

Path Synopsis
examples
redirect command
An example of how to use go-icap.
An example of how to use go-icap.
simple-server command
This example demonstrates how to create a basic ICAP server that handles REQMOD and RESPMOD requests.
This example demonstrates how to create a basic ICAP server that handles REQMOD and RESPMOD requests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL