פתרון קריאות מרוחקות וחוזי בקשה¶
פתרון התרגיל הראשון¶
הלקוח קורא לCompute שמוגדרת בclient stub. המימוש הזה מקודד את הפרמטרים ומפעיל את runtime הRPC. בצד השרת הstub מפרש את הנתונים ומגיע לפונקציה שלנו. שם זהה במקור אינו אומר שאותה פונקציה רצה בשני הצדדים.
עם יצירת stubs מתאימה והגדרת range, קלט מחוץ לטווח אמור להידחות במסלול הmarshalling או הunmarshalling לפני חישוב. נבדוק את אירוע הכניסה ולא נסתמך על מקום הדפסת השגיאה. נשמור גם אימות במימוש המצומצם שלנו עבור קריאה מקומית או שינוי עתידי של הגבול.
מחיקת רשומה דורשת זהות, מזהה אובייקט ומדיניות בעלות. טווח מספרי חוקי לא מוכיח שהמספר שייך ללקוח. את ההחלטה הזאת נבצע על הרשומה היציבה בזמן הפעולה, כפי שעשינו בpipe.
פתרון התרגיל השני¶
התוצאה תלויה בחוזה של הtransport, ולא רק בשאלה האם קראנו לפונקציית תצורה. התיעוד קובע שncalrpc משתמש ברמת packet privacy, ולמנגנון המקומי יש הגנות של מערכת ההפעלה. לכן הסרת RpcBindingSetAuthInfoW אינה בדיקה אמינה ליצירת לקוח ללא אימות. נשמור את נתוני הcallback ואת קוד התוצאה בפועל, בלי לכתוב מראש שהוא חייב להידחות.
המדיניות היישומית הנוספת יכולה להחזיר ERROR_ACCESS_DENIED כאשר n שווה 13, לפני חישוב. זוהי מדיניות מעבדה מלאכותית כדי להפריד שני שלבים: הruntime קיבל את הקריאה בהתאם להגנת הערוץ, והיישום עדיין רשאי לסרב לפעולה. במוצר נחליף את הכלל הזה בבדיקת זהות ובעלות שמתאימה למשאב.
ההגנות המקומיות אינן הבטחת אימות מרוחק. לפני העברה לTCP נגדיר ונבדוק את השירות, זהות השרת, רמת ההגנה והרשאות הפעולה מחדש. אפשר לקרוא את תיעוד רמות האימות ואת התנהגות האבטחה לפי transport כדי להבין מדוע אותה השמטה אינה ניסוי שקול בכל חיבור.
פתרון התרגיל השלישי¶
נגדיר מפתח מורכב מזהות מאומתת ומזהה בקשה. בתוך טרנזקציה נחפש רשומה קיימת: אם היא נמצאת ותוכן הבקשה תואם, נחזיר את התוצאה השמורה; אם התוכן שונה, נדחה שימוש חוזר במזהה. אם היא חדשה, נגדיל את המונה ונשמור את התוצאה עבור אותו מפתח באותה טרנזקציה.
הסדר הלוגי של הטרנזקציה נראה כך. זהו תיאור של פעולות אחסון, לא קוד של מנוע מסוים.
begin transaction
lookup (authenticated_client, request_id)
if present:
compare original request
return saved outcome after ending read transaction
else:
increment counter
save request identity + original input + outcome
commit transaction
send reply
אם הקריסה קרתה לפני commit, לא נשמרת אף אחת משתי הפעולות לפי חוזה הטרנזקציה. אם קרתה אחריו ולפני תשובה, ניסיון חוזר מוצא את התוצאה הקיימת. צריך גם למנוע משתי בקשות מקבילות לעבור יחד את בדיקת ההיעדר, למשל באמצעות unique constraint ורמת בידוד או נעילה מתאימה.
אם עדכון המונה ושמירת הdedup אינם אטומיים, קיימת נקודת כשל שבה המונה גדל אבל אין זכר לכך. שמירת הטבלה רק בRAM אינה פותרת את הבעיה אחרי restart. במקרה כזה נצטרך לשנות את האחסון, להפוך את הפעולה לidempotent בדרך אחרת או לחשוף מצב לא מוכרע ודרך בירור.
נגדיר גם משך שמירת תוצאות. מחיקת רשומות dedup ישנות פירושה שבקשה שחוזרת לאחר חלון השמירה יכולה להתבצע שוב, אלא אם מזהים ישנים נדחים באמצעות מנגנון נוסף. ההתחייבות "פעם אחת" צריכה לציין את החלון הזה ולא להסתתר מאחורי שם הAPI.