mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-24 13:58:12 -05:00
180 lines
5.2 KiB
Go
180 lines
5.2 KiB
Go
package icapclient
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// getStatusWithCode prepares the status code and status text from two given strings
|
|
func getStatusWithCode(str1, str2 string) (int, string, error) {
|
|
|
|
statusCode, err := strconv.Atoi(str1)
|
|
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
|
|
status := strings.TrimSpace(str2)
|
|
|
|
return statusCode, status, nil
|
|
}
|
|
|
|
// getHeaderVal parses the header and its value from a tcp message string
|
|
func getHeaderVal(str string) (string, string) {
|
|
|
|
headerVals := strings.SplitN(str, ":", 2)
|
|
header := headerVals[0]
|
|
val := ""
|
|
|
|
if len(headerVals) >= 2 {
|
|
val = strings.TrimSpace(headerVals[1])
|
|
}
|
|
|
|
return header, val
|
|
|
|
}
|
|
|
|
// isRequestLine determines if the tcp message string is a request line, i.e the first line of the message or not
|
|
func isRequestLine(str string) bool {
|
|
return strings.Contains(str, ICAPVersion) || strings.Contains(str, HTTPVersion)
|
|
}
|
|
|
|
// setEncapsulatedHeaderValue generates the Encapsulated values and assigns to the ICAP request string
|
|
func setEncapsulatedHeaderValue(icapReqStr *string, httpReqStr, httpRespStr string) {
|
|
encpVal := " "
|
|
|
|
if strings.HasPrefix(*icapReqStr, MethodOPTIONS) { // if the request method is OPTIONS
|
|
if httpReqStr == "" && httpRespStr == "" { // the most common case for OPTIONS method, no Encapsulated body
|
|
encpVal += "null-body=0"
|
|
} else {
|
|
encpVal += "opt-body=0" // if there is an Encapsulated body
|
|
}
|
|
}
|
|
|
|
if strings.HasPrefix(*icapReqStr, MethodREQMOD) || strings.HasPrefix(*icapReqStr, MethodRESPMOD) { // if the request method is RESPMOD or REQMOD
|
|
re := regexp.MustCompile(DoubleCRLF) // looking for the match of the string \r\n\r\n, as that is the expression that seperates each blocks, i.e headers and bodies
|
|
reqIndices := re.FindAllStringIndex(httpReqStr, -1) // getting the offsets of the matches, tells us the starting/ending point of headers and bodies
|
|
|
|
reqEndsAt := 0 // this is needed to calculate the response headers by adding the last offset of the request block
|
|
if reqIndices != nil {
|
|
encpVal += "req-hdr=0"
|
|
reqEndsAt = reqIndices[0][1]
|
|
if len(reqIndices) > 1 { // indicating there is a body present for the request block, as length would have been 1 for a single match of \r\n\r\n
|
|
encpVal += fmt.Sprintf(", req-body=%d", reqIndices[0][1]) // assigning the starting point of the body
|
|
reqEndsAt = reqIndices[1][1]
|
|
} else if httpRespStr == "" {
|
|
encpVal += fmt.Sprintf(", null-body=%d", reqIndices[0][1])
|
|
}
|
|
if httpRespStr != "" {
|
|
encpVal += ", "
|
|
}
|
|
}
|
|
|
|
respIndices := re.FindAllStringIndex(httpRespStr, -1)
|
|
|
|
if respIndices != nil {
|
|
encpVal += fmt.Sprintf("res-hdr=%d", reqEndsAt)
|
|
if len(respIndices) > 1 {
|
|
encpVal += fmt.Sprintf(", res-body=%d", reqEndsAt+respIndices[0][1])
|
|
} else {
|
|
encpVal += fmt.Sprintf(", null-body=%d", reqEndsAt+respIndices[0][1])
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
*icapReqStr = fmt.Sprintf(*icapReqStr, encpVal) // formatting the ICAP request Encapsulated header with the value
|
|
}
|
|
|
|
// replaceRequestURIWithActualURL replaces the just the escaped portion of the url with the entire URL in the dumped request message
|
|
func replaceRequestURIWithActualURL(str *string, uri, url string) {
|
|
if uri == "" {
|
|
uri = "/"
|
|
}
|
|
*str = strings.Replace(*str, uri, url, 1)
|
|
}
|
|
|
|
// addFullBodyInPreviewIndicator adds 0; ieof\r\n\r\n which indicates the entire body fitted in preview
|
|
func addFullBodyInPreviewIndicator(str *string) {
|
|
*str = strings.TrimSuffix(*str, DoubleCRLF)
|
|
*str += fullBodyEndIndicatorPreviewMode
|
|
}
|
|
|
|
// splitBodyAndHeader separates header and body from a http message
|
|
func splitBodyAndHeader(str string) (string, string, bool) {
|
|
ss := strings.SplitN(str, DoubleCRLF, 2)
|
|
|
|
if len(ss) < 2 || ss[1] == "" {
|
|
return "", "", false
|
|
}
|
|
|
|
headerStr := ss[0]
|
|
bodyStr := ss[1]
|
|
|
|
return headerStr, bodyStr, true
|
|
}
|
|
|
|
// bodyAlreadyChunked determines if the http body is already chunked from the origin server or not
|
|
func bodyAlreadyChunked(str string) bool {
|
|
_, bodyStr, ok := splitBodyAndHeader(str)
|
|
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
r := regexp.MustCompile("\\r\\n0(\\r\\n)+$")
|
|
return r.MatchString(bodyStr)
|
|
|
|
}
|
|
|
|
// parsePreviewBodyBytes parses the preview portion of the body and only keeps that in the message
|
|
func parsePreviewBodyBytes(str *string, pb int) {
|
|
|
|
headerStr, bodyStr, ok := splitBodyAndHeader(*str)
|
|
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
bodyStr = bodyStr[:pb]
|
|
|
|
*str = headerStr + DoubleCRLF + bodyStr
|
|
}
|
|
|
|
// addHexaBodyByteNotations adds the hexadecimal byte notaions in the messages
|
|
// for example: Hello World, becomes
|
|
// b
|
|
// Hello World
|
|
// 0
|
|
func addHexaBodyByteNotations(bodyStr *string) {
|
|
|
|
bodyBytes := []byte(*bodyStr)
|
|
|
|
*bodyStr = fmt.Sprintf("%x%s%s%s", len(bodyBytes), CRLF, *bodyStr, bodyEndIndicator)
|
|
}
|
|
|
|
// mergeHeaderAndBody merges the header and body of the http message togather
|
|
func mergeHeaderAndBody(src *string, headerStr, bodyStr string) {
|
|
*src = headerStr + DoubleCRLF + bodyStr
|
|
}
|
|
|
|
func chunkBodyByBytes(bdyByte []byte, cl int) []byte {
|
|
|
|
newBytes := []byte{}
|
|
|
|
for i := 0; i < len(bdyByte); i += cl {
|
|
end := i + cl
|
|
if end > len(bdyByte) {
|
|
end = len(bdyByte)
|
|
}
|
|
|
|
newBytes = append(newBytes, []byte(fmt.Sprintf("%x\r\n", len(bdyByte[i:end]))+string(bdyByte[i:end]))...)
|
|
}
|
|
|
|
newBytes = append(newBytes, []byte(bodyEndIndicator)...)
|
|
|
|
return newBytes
|
|
}
|