| 53 | } |
| 54 | |
| 55 | func (s *Session) Init(w http.ResponseWriter, r *http.Request, maxBodySize int64) error { |
| 56 | s.requestID = uid.New(uid.RequestPrefix) |
| 57 | |
| 58 | // Wrap ResponseWriter with status recorder |
| 59 | s.w = &statusRecorder{ |
| 60 | ResponseWriter: w, |
| 61 | statusCode: 0, // Default to 0, this should always be overwritten by the metrics middleware |
| 62 | written: false, |
| 63 | } |
| 64 | |
| 65 | s.r = r |
| 66 | s.logRequestToClickHouse = true // Default to logging requests to ClickHouse |
| 67 | |
| 68 | // Apply body size limit if configured |
| 69 | // Note: MaxBytesReader needs the original unwrapped ResponseWriter, so we pass w directly |
| 70 | if maxBodySize > 0 { |
| 71 | s.r.Body = http.MaxBytesReader(w, s.r.Body, maxBodySize) |
| 72 | } |
| 73 | |
| 74 | // For gRPC/streaming requests, skip body buffering — the body is a stream |
| 75 | // that must be forwarded incrementally. Buffering would deadlock because the |
| 76 | // client waits for a response before sending more frames. |
| 77 | if IsStreamingContentType(r.Header.Get("Content-Type")) { |
| 78 | s.requestBody = nil |
| 79 | } else { |
| 80 | // Read and cache the request body so metrics middleware can access it even on early errors. |
| 81 | // We need to replace r.Body with a fresh reader afterwards so other middleware |
| 82 | // can still read the body if necessary. |
| 83 | var err error |
| 84 | s.requestBody, err = io.ReadAll(s.r.Body) |
| 85 | closeErr := s.r.Body.Close() |
| 86 | |
| 87 | // Handle read errors (including MaxBytesError) |
| 88 | if err != nil { |
| 89 | // Check if this is a MaxBytesError from http.MaxBytesReader |
| 90 | var maxBytesErr *http.MaxBytesError |
| 91 | if errors.As(err, &maxBytesErr) { |
| 92 | return fault.Wrap(err, |
| 93 | fault.Code(codes.User.BadRequest.RequestBodyTooLarge.URN()), |
| 94 | fault.Internal(fmt.Sprintf("request body exceeds size limit of %d bytes", maxBytesErr.Limit)), |
| 95 | fault.Public(fmt.Sprintf("The request body exceeds the maximum allowed size of %d bytes.", maxBytesErr.Limit)), |
| 96 | ) |
| 97 | } |
| 98 | |
| 99 | return fault.Wrap(err, |
| 100 | fault.Code(codes.User.BadRequest.RequestBodyUnreadable.URN()), |
| 101 | fault.Internal("unable to read request body"), |
| 102 | fault.Public("The request body could not be read."), |
| 103 | ) |
| 104 | } |
| 105 | |
| 106 | // Handle close error (incase that ever happens) |
| 107 | if closeErr != nil { |
| 108 | return fault.Wrap(closeErr, |
| 109 | fault.Internal("failed to close request body"), |
| 110 | fault.Public("An error occurred processing the request."), |
| 111 | ) |
| 112 | } |