mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-24 22:08:58 -05:00
161 lines
4.0 KiB
Go
161 lines
4.0 KiB
Go
package icapclient
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
// Request represents the icap client request data
|
|
type Request struct {
|
|
Method string
|
|
URL *url.URL
|
|
Header http.Header
|
|
HTTPRequest *http.Request
|
|
HTTPResponse *http.Response
|
|
ChunkLength int
|
|
PreviewBytes int
|
|
ctx *context.Context
|
|
previewSet bool
|
|
bodyFittedInPreview bool
|
|
remainingPreviewBytes []byte
|
|
}
|
|
|
|
// NewRequest is the factory function for Request
|
|
func NewRequest(method, urlStr string, httpReq *http.Request, httpResp *http.Response) (*Request, error) {
|
|
|
|
method = strings.ToUpper(method)
|
|
|
|
u, err := url.Parse(urlStr)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req := &Request{
|
|
Method: method,
|
|
URL: u,
|
|
Header: make(map[string][]string),
|
|
HTTPRequest: httpReq,
|
|
HTTPResponse: httpResp,
|
|
}
|
|
|
|
if err := req.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return req, nil
|
|
}
|
|
|
|
// DumpRequest returns the given request in its ICAP/1.x wire
|
|
// representation.
|
|
func DumpRequest(req *Request) ([]byte, error) {
|
|
|
|
// Making the ICAP message block
|
|
|
|
reqStr := fmt.Sprintf("%s %s %s%s", req.Method, req.URL.String(), ICAPVersion, CRLF)
|
|
|
|
for headerName, vals := range req.Header {
|
|
for _, val := range vals {
|
|
reqStr += fmt.Sprintf("%s: %s%s", headerName, val, CRLF)
|
|
}
|
|
}
|
|
|
|
reqStr += "Encapsulated: %s" + CRLF // will populate the Encapsulated header value after making the http Request & Response messages
|
|
reqStr += CRLF
|
|
|
|
// Making the HTTP Request message block
|
|
|
|
httpReqStr := ""
|
|
if req.HTTPRequest != nil {
|
|
b, err := httputil.DumpRequestOut(req.HTTPRequest, true)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
httpReqStr += string(b)
|
|
replaceRequestURIWithActualURL(&httpReqStr, req.HTTPRequest.URL.EscapedPath(), req.HTTPRequest.URL.String())
|
|
|
|
if req.Method == MethodREQMOD {
|
|
if req.previewSet {
|
|
parsePreviewBodyBytes(&httpReqStr, req.PreviewBytes)
|
|
}
|
|
|
|
if !bodyAlreadyChunked(httpReqStr) {
|
|
headerStr, bodyStr, ok := splitBodyAndHeader(httpReqStr)
|
|
if ok {
|
|
addHexaBodyByteNotations(&bodyStr)
|
|
mergeHeaderAndBody(&httpReqStr, headerStr, bodyStr)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if httpReqStr != "" { // if the HTTP Request message block doesn't end with a \r\n\r\n, then going to add one by force for better calculation of byte offsets
|
|
for !strings.HasSuffix(httpReqStr, DoubleCRLF) {
|
|
httpReqStr += CRLF
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Making the HTTP Response message block
|
|
|
|
httpRespStr := ""
|
|
if req.HTTPResponse != nil {
|
|
b, err := httputil.DumpResponse(req.HTTPResponse, true)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
httpRespStr += string(b)
|
|
|
|
if req.previewSet {
|
|
parsePreviewBodyBytes(&httpRespStr, req.PreviewBytes)
|
|
}
|
|
|
|
if !bodyAlreadyChunked(httpRespStr) {
|
|
headerStr, bodyStr, ok := splitBodyAndHeader(httpRespStr)
|
|
if ok {
|
|
addHexaBodyByteNotations(&bodyStr)
|
|
mergeHeaderAndBody(&httpRespStr, headerStr, bodyStr)
|
|
}
|
|
}
|
|
|
|
if httpRespStr != "" && !strings.HasSuffix(httpRespStr, DoubleCRLF) { // if the HTTP Response message block doesn't end with a \r\n\r\n, then going to add one by force for better calculation of byte offsets
|
|
httpRespStr += CRLF
|
|
}
|
|
|
|
}
|
|
|
|
if encpVal := req.Header.Get(EncapsulatedHeader); encpVal != "" {
|
|
reqStr = fmt.Sprintf(reqStr, encpVal)
|
|
} else {
|
|
//populating the Encapsulated header of the ICAP message portion
|
|
setEncapsulatedHeaderValue(&reqStr, httpReqStr, httpRespStr)
|
|
}
|
|
|
|
// determining if the http message needs the full body fitted in the preview portion indicator or not
|
|
if httpRespStr != "" && req.previewSet && req.bodyFittedInPreview {
|
|
addFullBodyInPreviewIndicator(&httpRespStr)
|
|
}
|
|
|
|
if req.Method == MethodREQMOD && req.previewSet && req.bodyFittedInPreview {
|
|
addFullBodyInPreviewIndicator(&httpReqStr)
|
|
}
|
|
|
|
data := []byte(reqStr + httpReqStr + httpRespStr)
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// SetContext sets a context for the ICAP request
|
|
func (r *Request) SetContext(ctx context.Context) { // TODO: make context take control over the whole operation
|
|
r.ctx = &ctx
|
|
}
|