diff --git a/go/src/restincl/workerpool.go b/go/src/restincl/workerpool.go index 4899b1c..3db1aa9 100644 --- a/go/src/restincl/workerpool.go +++ b/go/src/restincl/workerpool.go @@ -70,6 +70,10 @@ var M_freechan chan int //List of free channels submitted by wokers var M_ctxs []*atmi.ATMICtx //List of contexts +// Has leading circumflex, but not trailing dollar, because we need first value in X-Forwarded-For header and do not need port number in RemoteAddr. +// Groups order matters (start with longer numbers, otherwise trailing digits are lost). +var M_ipv4AddressRx, _ = regexp.Compile("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])") + // Generates a file form a Base64 string and writes it to response func generateFileFromBase64(fileContentsB64 string, tmpFileName string, w http.ResponseWriter) { decodedFileContent, err := base64.StdEncoding.DecodeString(fileContentsB64) @@ -96,6 +100,19 @@ func generateFileFromBase64(fileContentsB64 string, tmpFileName string, w http.R os.Remove(tmpFileName) } +// Generates a file from a path and writes it to response +func generateFileFromPath(filePath string) []byte { + // Read file + fileContents, err := os.ReadFile(filePath) + dec64 := base64.StdEncoding.EncodeToString([]byte(fileContents)) // We send files as base64, so we need to encode + + if err != nil { + panic(err) + } + + return []byte(dec64) +} + //Generate the headers for UBF mode and for EXT mode //Return content type if available func genRspHeaders(ac *atmi.ATMICtx, bufu *atmi.TypedUBF, w http.ResponseWriter, @@ -379,8 +396,13 @@ func genRsp(ac *atmi.ATMICtx, buf atmi.TypedBuffer, svc *ServiceMap, var errU atmi.UBFError rsp, errU = bufu.BGetByteArr(ubftab.EX_IF_RSPDATA, 0) - if svc.Stream { - generateFileFromBase64(string(rsp), "./tempfilename", w) + + if bufu.BPres(ubftab.EX_IF_RSPFILEACTION, 0) { + rsp = generateFileFromPath(string(rsp)) + } else { + if svc.Stream { + generateFileFromBase64(string(rsp), "./tempfilename", w) + } } if nil != errU { @@ -691,6 +713,10 @@ func genRsp(ac *atmi.ATMICtx, buf atmi.TypedBuffer, svc *ServiceMap, header = obj["Header"].(map[string]interface{}) delete(obj, "Header") } + + // Remove address field from response JSON. + delete(obj, "RemoteAddr") + //Add headers to ResponseWriter for k, v := range header { for _, val := range v.([]interface{}) { @@ -847,21 +873,26 @@ func genRsp(ac *atmi.ATMICtx, buf atmi.TypedBuffer, svc *ServiceMap, func parseHeaders(ac *atmi.ATMICtx, svc *ServiceMap, req *http.Request, bufu *atmi.TypedUBF) atmi.UBFError { + if errU := bufu.BAdd(ubftab.EX_NETTHEIRIP, getRemoteAddr(ac, req)); nil != errU { + return errU + } + // Add header data to UBF fields if svc.Parseheaders { - for k, v := range req.Header { - ac.TpLogDebug("Header field %s, Value %+v", k, v) - hv := fmt.Sprintf("%s", v) - if errU := bufu.BAdd(ubftab.EX_IF_REQHN, k); nil != errU { - return errU - } - - if errU := bufu.BAdd(ubftab.EX_IF_REQHV, hv); nil != errU { - return errU + for fld, hdr := range req.Header { + /* Bug #800 had header values with [] */ + for _, val := range hdr { + ac.TpLogDebug("Header field %s, Value %s", fld, val) + if errU := bufu.BAdd(ubftab.EX_IF_REQHN, fld); nil != errU { + return errU + } + if errU := bufu.BAdd(ubftab.EX_IF_REQHV, val); nil != errU { + return errU + } } - // Add Cookies data to UBF } + // Add Cookies data to UBF if svc.Parsecookies { for _, cookie := range req.Cookies() { // Incoming request have Name and Value @@ -880,7 +911,23 @@ func parseHeaders(ac *atmi.ATMICtx, svc *ServiceMap, req *http.Request, return nil } -//Common func for parsing query parameters and loading them into UBF buffer +// Returns IPv4 address of client performin request, if available. Empty string otherwise. +func getRemoteAddr(ac *atmi.ATMICtx, req *http.Request) string { + // Add 'proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' to Nginx site config to have this header (disabled by default). + // Header name casing does not matter. + fwdAddress := req.Header.Get("X-Forwarded-For") + if fwdAddress != "" { + ac.TpLogDebug("Client address connection [%s], forwarded [%s]", req.RemoteAddr, fwdAddress) + // First one is the most external. Separate by ", ", if more than one. + return M_ipv4AddressRx.FindString(fwdAddress) + } + + ac.TpLogDebug("Client address connection [%s]", req.RemoteAddr) + // IP:port, we return only IP + return M_ipv4AddressRx.FindString(req.RemoteAddr) +} + +// Common func for parsing query parameters and loading them into UBF buffer func parseQuery(ac *atmi.ATMICtx, svc *ServiceMap, req *http.Request, bufu *atmi.TypedUBF) atmi.UBFError { @@ -1232,6 +1279,8 @@ func handleMessage(ac *atmi.ATMICtx, svc *ServiceMap, w http.ResponseWriter, // Add header data to UBF fields if svc.Parseheaders { + obj["RemoteAddr"] = getRemoteAddr(ac, req) + if svc.JsonHeaderField != "" { obj[svc.JsonHeaderField] = req.Header } else {