forked from egirna/icap-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrequest.go
160 lines (123 loc) · 4.03 KB
/
request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
}