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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//! GitHub webhook processing and validation.
//!
//! This module provides comprehensive webhook handling for GitHub Apps, including
//! signature validation, event processing, and async handler execution using a
//! fire-and-forget pattern to ensure fast HTTP responses.
//!
//! # Core Components
//!
//! - [`WebhookHandler`] - Trait for application-provided event processing logic
//! - [`WebhookReceiver`] - HTTP webhook intake with validation and async dispatch
//! - [`SignatureValidator`] - HMAC-SHA256 signature validation
//! - [`WebhookRequest`]/[`WebhookResponse`] - HTTP request/response types
//!
//! # Fire-and-Forget Pattern
//!
//! The receiver ensures GitHub receives responses within the 10-second timeout:
//!
//! 1. Validate signature (< 10ms)
//! 2. Process/normalize event (< 5ms)
//! 3. Return HTTP response immediately (target < 100ms)
//! 4. Spawn async tasks for handlers (non-blocking)
//!
//! # Security
//!
//! Webhook signature validation uses HMAC-SHA256 with constant-time comparison
//! to prevent timing attacks. All validation operations complete in under 100ms.
//!
//! # Complete Usage Example
//!
//! ## Basic Webhook Handler
//!
//! ```rust,no_run
//! use github_bot_sdk::webhook::{WebhookHandler, WebhookReceiver, WebhookRequest};
//! use github_bot_sdk::auth::SecretProvider;
//! use github_bot_sdk::events::{EventProcessor, ProcessorConfig, EventEnvelope};
//! use async_trait::async_trait;
//! use std::sync::Arc;
//! use std::collections::HashMap;
//!
//! // Define your handler
//! struct MyBotHandler;
//!
//! #[async_trait]
//! impl WebhookHandler for MyBotHandler {
//! async fn handle_event(
//! &self,
//! envelope: &EventEnvelope,
//! ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! match envelope.event_type.as_str() {
//! "pull_request" => {
//! println!("Processing PR event: {}", envelope.event_id);
//! // Add your PR processing logic here
//! }
//! "issues" => {
//! println!("Processing issue event: {}", envelope.event_id);
//! // Add your issue processing logic here
//! }
//! _ => {
//! println!("Ignoring event type: {}", envelope.event_type);
//! }
//! }
//! Ok(())
//! }
//! }
//!
//! # async fn example(secret_provider: Arc<dyn SecretProvider>) -> Result<(), Box<dyn std::error::Error>> {
//! // Setup receiver with processor
//! let processor = EventProcessor::new(ProcessorConfig::default());
//! let mut receiver = WebhookReceiver::new(secret_provider, processor);
//!
//! // Register your handler
//! receiver.add_handler(Arc::new(MyBotHandler)).await;
//!
//! // Process incoming webhook (typically called from HTTP server)
//! let headers = HashMap::from([
//! ("x-github-event".to_string(), "pull_request".to_string()),
//! ("x-github-delivery".to_string(), "12345-67890".to_string()),
//! ("x-hub-signature-256".to_string(), "sha256=abc...".to_string()),
//! ]);
//! let body = bytes::Bytes::from_static(b"{\"action\":\"opened\",\"number\":1}");
//! let request = WebhookRequest::new(headers, body);
//!
//! let response = receiver.receive_webhook(request).await;
//! println!("Response status: {}", response.status_code());
//! # Ok(())
//! # }
//! ```
//!
//! ## Multiple Handlers
//!
//! ```rust,no_run
//! use github_bot_sdk::webhook::{WebhookHandler, WebhookReceiver};
//! use github_bot_sdk::events::EventEnvelope;
//! use async_trait::async_trait;
//! use std::sync::Arc;
//!
//! // Define specialized handlers
//! struct PullRequestHandler;
//! struct IssueHandler;
//! struct SecurityHandler;
//!
//! #[async_trait]
//! impl WebhookHandler for PullRequestHandler {
//! async fn handle_event(&self, envelope: &EventEnvelope) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! if envelope.event_type == "pull_request" {
//! println!("PR Handler: Processing {}", envelope.event_id);
//! // PR-specific logic
//! }
//! Ok(())
//! }
//! }
//!
//! #[async_trait]
//! impl WebhookHandler for IssueHandler {
//! async fn handle_event(&self, envelope: &EventEnvelope) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! if envelope.event_type == "issues" {
//! println!("Issue Handler: Processing {}", envelope.event_id);
//! // Issue-specific logic
//! }
//! Ok(())
//! }
//! }
//!
//! #[async_trait]
//! impl WebhookHandler for SecurityHandler {
//! async fn handle_event(&self, envelope: &EventEnvelope) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! // Security monitoring across all event types
//! println!("Security: Auditing event {}", envelope.event_id);
//! Ok(())
//! }
//! }
//!
//! # async fn example(mut receiver: WebhookReceiver) -> Result<(), Box<dyn std::error::Error>> {
//! // Register multiple handlers - all will be invoked concurrently
//! receiver.add_handler(Arc::new(PullRequestHandler)).await;
//! receiver.add_handler(Arc::new(IssueHandler)).await;
//! receiver.add_handler(Arc::new(SecurityHandler)).await;
//! # Ok(())
//! # }
//! ```
//!
//! ## HTTP Server Integration (Axum Example)
//!
//! ```rust,ignore
//! use github_bot_sdk::webhook::{WebhookReceiver, WebhookRequest, WebhookResponse};
//! use axum::{
//! extract::State,
//! http::{HeaderMap, StatusCode},
//! response::{IntoResponse, Response},
//! Json, Router,
//! routing::post,
//! };
//! use bytes::Bytes;
//! use std::sync::Arc;
//!
//! // Application state with receiver
//! #[derive(Clone)]
//! struct AppState {
//! receiver: Arc<WebhookReceiver>,
//! }
//!
//! // HTTP handler for webhook endpoint
//! async fn handle_webhook(
//! State(state): State<AppState>,
//! headers: HeaderMap,
//! body: Bytes,
//! ) -> Response {
//! // Convert HTTP headers to HashMap
//! let header_map: std::collections::HashMap<String, String> = headers
//! .iter()
//! .map(|(k, v)| (k.as_str().to_lowercase(), v.to_str().unwrap_or("").to_string()))
//! .collect();
//!
//! // Create webhook request
//! let request = WebhookRequest::new(header_map, body);
//!
//! // Process webhook
//! let response = state.receiver.receive_webhook(request).await;
//!
//! // Convert to HTTP response
//! let status = StatusCode::from_u16(response.status_code()).unwrap();
//! let message = response.message().to_string();
//!
//! (status, Json(serde_json::json!({
//! "message": message
//! }))).into_response()
//! }
//!
//! # async fn example(receiver: Arc<WebhookReceiver>) -> Result<(), Box<dyn std::error::Error>> {
//! let state = AppState { receiver };
//!
//! let app = Router::new()
//! .route("/webhook", post(handle_webhook))
//! .with_state(state);
//!
//! // Run server
//! // let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
//! // axum::serve(listener, app).await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Direct Signature Validation
//!
//! If you need to validate signatures independently:
//!
//! ```rust,no_run
//! use github_bot_sdk::webhook::SignatureValidator;
//! use github_bot_sdk::auth::SecretProvider;
//! use std::sync::Arc;
//!
//! # async fn example(secret_provider: Arc<dyn SecretProvider>) -> Result<(), Box<dyn std::error::Error>> {
//! let validator = SignatureValidator::new(secret_provider);
//!
//! let payload = b"{\"action\":\"opened\",\"number\":1}";
//! let signature = "sha256=5c4a..."; // From X-Hub-Signature-256 header
//!
//! let is_valid = validator.validate(payload, signature).await?;
//! if is_valid {
//! println!("Webhook signature is valid");
//! } else {
//! println!("Invalid webhook signature - possible tampering");
//! }
//! # Ok(())
//! # }
//! ```
// Re-export main types
pub use WebhookHandler;
pub use ;
pub use SignatureValidator;